This documentation is still a work-in-progress.
PR: https://github.com/DSpace/dspace-angular/pull/3994


The menu sections will be generated by multiple “Providers” which will each add a section or list of related sections. These providers will then be added to the list of providers per menu defined in the app.menus.ts file.

export const MENUS = buildMenuStructure({  
  [MenuID.PUBLIC]: [  
    CommunityListMenuProvider,  
    BrowseMenuProvider,  
    StatisticsMenuProvider,  
  ],  
  [MenuID.ADMIN]: [  
    NewMenuProvider,  
    EditMenuProvider,  
    ... 
    SystemWideAlertMenuProvider,  
  ],  
  [MenuID.DSO_EDIT]: [  
    DsoOptionMenuProvider.withSubs([  
      SubscribeMenuProvider.onRoute(  
        MenuRoute.SIMPLE_COMMUNITY_PAGE,  
        MenuRoute.SIMPLE_COLLECTION_PAGE,  
      ),  
      ...
      ClaimMenuProvider.onRoute(  
        MenuRoute.SIMPLE_ITEM_PAGE,  
        MenuRoute.FULL_ITEM_PAGE,  
      ),  
    ]),  
  ],  
});

This approach makes it way more intuitive to see what kind of sections there are and to add new sections in between already existing ones.

This new version fully removes the old way of adding route based data (e.g. statistics) and replaces it with a new way where you can easily add routes to specified providers and/or retrieve route data in the provider to be used for its sections. It also adds support to separately define a parent sections with various child sections. The latter will be mostly useful for creating sections on the DSO pages where you can now separately define the parent section through a provider and then add child providers for the various different sections.

For example, in the current implementation all the buttons on the DSO pages have been added to a single parent parent section resulting in a single button on the DSO pages with the relevant sections in the dropdown. In case sections need to be moved, the provider can simply be moved to a different location such as before the provider responsible for providing the button (DsoOptionMenuProvider).

Finally, since the menus have been moved from the resolvers to the InitService, the menus will not delay the page from loading when the menu sections need some time. The menus will simply appear when they are fully loaded. To prevent "blinking" when changing pages, the sections that need to be added/removed are all done at the same time.

Provider/section properties and helper providers

Provider properties

The following properties can be set on the level of a provider when creating a new one:

Section properties

The sections still contain the same configuration properties as before the refactor, however a lot of the properties will be already set by the providers. There are however some parts that still need to be provided per sections:

The following properties can also be set on the sections to overwrite the default behaviour added by the provider:

The following two properties can also be set but should be avoided since these are set automatically and may cause confusion when overwritten manually:

Helper providers

To make it easier to create new providers, several "helper" providers have been created that will pre-configure some aspects of the provider and two static methods can be used on any type of provider to change the behaviour of the provider.

Static methods

The following two methods can be added to a provider when defining it in the app.menus.ts file:

onRoute
To allow people to easily determine routes on which they want to add the sections from this provider, a new route ID has been added to various routes. All of the existing route IDs can be found in MenuRoute . These IDs can then be added to the route in the following way:

{
  path: '',
  component: ThemedCommunityPageComponent,
  pathMatch: 'full',
  data: {
    menuRoute: MenuRoute.SIMPLE_COMMUNITY_PAGE,
  },
}


On a provider, the onRoute static method can be used which takes a list of route IDs as argument and will only add menus on the routes which contain the provided route ID in its route data.

The shouldPersistOnRouteChange will be automatically set to false since the sections of this provider depends on the route.

withSubs
This method takes a list of child providers as argument.

When this method is called on a provider, this provider will act as a parent provider to all the child providers that have been provided as argument. The parentID will be automatically set for all the child providers.

When using the withSubs method, avoid adding multiple sections to the parent provider since this can cause unnecessary confusion. In case multiple sections have been added to the parent, the sub provider sections will be added to the first section

Note:
These two methods are mutually exclusive. In case you would want to combine the withSubs and onRoute, the onRoute method can instead be used on the child providers. If alwaysRenderExpandable is set to true for the parent provider, the section will be hidden when no child sections are visible!

Extendable helper providers

The following abstract providers have been added that can be extended to more easily create certain menu section structures.

The following abstract providers exist:

AbstractRouteContextMenuProvider<T>
This provider contains some extra support to retrieve information from the route through the use of additional (abstract) methods:

DSpaceObjectPageMenuProvider
This provider extends the AbstractRouteContextMenuProvider with <T> being a DSpaceObject. It can be extended to easily create sections for various DSpace objects or a specific kind of DSpace objects.

The provider already implemented the getRouteContext to retrieve the DSpace object from the route data (route.data.dso). When implementing the getSectionsForContext method, the DSpace object present in the route object will be provided so it can be used to easily create the sections.

The provider extends isApplicable to return true only when a DSpace object was retrieved from the route. This can be further extended though in case only a specific type or entity needs to be supported.

AbstractExpandableMenuProvider
This provider contains support to easily create an expandable section with subsections.

It adds two abstract methods to be implemented:

The provider will then combine these sections and assign the correct parent IDs to the children. To manually interact with the ids of the parent and the child sections, the following two methods have been added to retrieve the sections:

How to migrate

The main idea of adding new sections to the existing menus is to separate the sections into different providers and then list these providers in the app.menus.ts file. Multiple sections can be provided by a single provider, but this should only be done if those sections are related to each other and are easy to add to the same provider, e.g. NewMenuProvider which contains the "New" section in the admin side bar and all its children. However, the various DSO pages have their menus split over separate providers, using the .withSubs method. In case more menus need to be added in the future, they can be easily added to the already existing dropdown, or added as separate buttons.

Below the practical steps are explained on how to migrate the sections.

Step 1

Choose how you want to migrate a section or a couple of sections that belong together, what helper provider you want to start from and what static methods you want to use if any.

For example:

Do you have just one section without route dependent info?
-> Extend the basic AbstractMenuProvider class. See SystemWideAlertMenuProvider for an example of this.

Do you have a simple expandable list without route dependent info?
-> Extend the AbstractExpandableMenuProvider. See ImportMenuProvider for an example of this.

Do you have a more complex expandable list with route dependent sections that need to be displayed on the DSpace object pages?
-> Create a separate parent provider extending the DSpaceObjectPageMenuProvider. Use the .withSubs method on this provider and create separate child providers that can also extend DSpaceObjectPageMenuProvider provider. On the child providers, the onRoute method can be used to determine on what routes these should be present.
See the DsoOptionMenuProvider and its children for an example of this.

Step 2

After having determined what abstract provider to start from, you can check if you need to overwrite some of the default properties of the provider that will be used for all sections added in this provider (shouldPersistOnRouteChange and alwaysRenderExpandable).

For example, in the DsoOptionMenuProvider, the alwaysRenderExpandable is set to true since the parent menu should be hidden when none of the children are present on the route.

Next, the relevant "getSections" method needs to be implemented (e.g. getSections, getSectionsForContext, getTopSection and getSubSections, ... )

These methods should return and observable of section(s). It is advised to only add the minimal information for the sections since a lot of the information will be automatically set.

The following properties are required:

The following can be set if the behaviour of the provider needs to be overwritten on section level:

Avoid manually setting the parentID and id properties!
The parentID will be set automatically when using .withSubs or AbstractExpandableMenuProvider. Use the getAutomatedSectionId or getAutomatedSectionIdForTopSection/getAutomatedSectionIdForSubsection methods when interactions are needed with a specific menu.
See ClaimMenuProvider for an example of the usage of getAutomatedSectionId.

Step 3

The final step is to add the new provider to the list of providers in the app.menus.ts file. The position of the provider will determine the order of display in the actual menu.