The new Navigation-Concept of Sulu 2

The next sprint is finished and so it’s time for a short summary of what has been happening during the last two weeks. First of all, our main goal of implementing a column tree view in order to be able to navigate through all web-spaces and its containing pages has been accomplished successfully. Furthermore, we have also integrated our new navigation and all of its visual components into sulu-cmf (content management framework).

Redesign Redesign

Originally the new navigation of Sulu 2.0 should have been one of the key features of the new CMF. As it turns out, the navigation was not just innovative but also good looking, including some nifty visual effects. With the new navigation we wanted to improve of the column tree and be able to navigate through all web-spaces just with the navigation. But after some usability tests and practical application, we observed that the concept often led to confusion and surprisingly wasn’t very user friendly when using very simple navigational structures.

Column navigation Column navigation

That’s why we changed the navigation to a new, more classical layout, as shown in Fig.1 and reintroduced a colu¬mn tree navigation, as shown in Fig.2. Still the main navigation items are representations of the bundles, which are able to be contracted and expanded. Its sub items should not exceed the depth of one. The Web Spaces - formerly called Portals - are now listed in an extra section, which enables a better user experience. The header bar, which also was part of the old navigational concept, has been improved, and it’s replacement - the edit-toolbar- will only show up when needed.

Apart from implementing the new UI components, the integration into Sulu presented itself as a challenge. The relocation of the content tab from the navigation to be included onto the page content turned out to be one of the main challenges.

Since the content tabs should only reload a part of the page and also be capable to be used by different bundles, a nesting mechanism of components is necessary. Let’s take a closer look at this:

When the user enters a URL, backbone is matching it against the ones which are provided by the bundle’s main.js files. Such a main.js file is defining, which components are called, when a certain URL was entered. Let’s take the SuluContactBundle as an example. The following code starts the contacts component main file with the option display: ‘list’ when the route 'contacts/contacts’ is called.

sandbox.mvc.routes.push({
  route: 'contacts/contacts',
    callback: function(){
      this.html('<div data-aura-component="contacts@sulucontact" 
      data-aura-display="list"/>');
    }
});
Tab component process Tab component process

A more advanced example is when the details form component of a contact is called as shown in Fig.3 Originally you would have done this by taking the same aura-component as in the example above and define the aura-display as 'form’. But since we want to include the tabs and the new edit toolbar (from now on called the header area), we introduced a new component type called 'content’. This component is starting the header area and then loads the contacts component as described above.

A more advanced example is when the details form component of a contact is called as shown in Fig.3 Originally you would have done this by taking the same aura-component as in the example above and define the aura-display as 'form’. But since we want to include the tabs and the new edit toolbar (from now on called the header area), we introduced a new component type called 'content’. This component is starting the header area and then loads the contacts component as described above.

sandbox.mvc.routes.push({
  route: 'contacts/contacts/edit::id/:content',
    callback: function(id, content){
      this.html('<div data-aura- 
        component="contacts/components/content@sulucontact" 
        data-aura-content="'+content+'" data-aura-id="' +id + '"/>'
      );
    }
});

So why do we need this intermediate step? It’s because on one side the header-area should not be reloaded when clicking a tab and on the other side the header area should also be loaded when a component from another bundle which extends a certain bundle is called. In our example the contacts component is getting extended by the permissions component of the SuluSecurityBundle. By calling contacts/contacts/edit:1/permissions’ the pattern above is fulfilled and therefore will call the content component of SuluContactBundle. There, the tabs component will select the permissions tab by matching the url and finally the permissions component of SuluSecurityBundle is called.

The component called by the tabs is delivered by the server. Therefore the Bundle has to implement the Content Navigation as described here. Most important for our purpose is that ContentComponent option is set.

Conclusion

With the new navigation layout we accomplish a clearer and more intuitive way of navigating through Sulu 2.0. In addition we accomplish a flexible way of extending components of different bundles. And finally we also bring a little bit of color back into the framework ;)