Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

when to use child states vs multiple views with ui-router

Last hours I have been reading through docs of UI-Router. But I can't find a solution for my problem.

My webapp has two different columns, a list on the left and a detail view on the right. Selecting a element of the list should show detail information on the right.

Which of this two approaches described in the title would you prefer? When to use what?

like image 898
Florian Mozart Avatar asked Aug 13 '14 06:08

Florian Mozart


People also ask

Are views and routes tied to the browser URL?

As with ng-Route, with this approach views and routes have not tied to the browser URL. You can change the view even if the browser URL does not change. Suggested read:

How to change the content of a UI-view?

Notice, in the app.js file how each state contains one or more named views and how each named view is injected in the ui-view declared in its name. Please consider the code in the Plunker, if you navigate to the “Example Tabs” and then click on the “Content tab” you will see a “Replace Content” button.

What is the default file format for UI-router?

This sample is heavily borrowed from AngularJS Routing Using UI-Router. So, here is our default file: index.html: This is the result of the codes in this tutorial.

What is a UI-router view?

The solution comes with ui-router itself, under the name of ( multiple ) named views. In practice, instead of just decorating a state with a single view (and a controller) we consider a state as, potentially, made of many views. Each view will be identified via its own name and will have its own controller (and data).


1 Answers

In fact, the List x Detail scenario is the most suitable for ui-router. These are in fact two states, the parent/child (i.e. child states to answer the question):

  • a List view (e.g. the left column). This could be a dynamic view, with paging, sorting and filtering, but still - this will always be a gateway, a parent to:
  • a Detail view (e.g. the right column). To select a detail (unless navigating via url directly) we simply need a List view. To select different detail, we can profit from a fact, that the parent/List view state is not reloading, while iterating among many details...

The best we can do is to observe the example, provided by ui-router team:

  • http://angular-ui.github.io/ui-router/sample/#/contacts

And we can also see its definition, which is part of this states definition:

  • https://github.com/angular-ui/ui-router/blob/master/sample/app/contacts/contacts.js

this link, belongs to the best documented pieces of code I do remember... It explains everything and also helps to learn how the ui-router state definition is working.

Below I tried to show that power by citing the definition of the List and Detail states.

The List state:

/////////////////////
// Contacts > List //
/////////////////////
// Using a '.' within a state name declares a child within a parent.
// So you have a new state 'list' within the parent 'contacts' state.
.state('contacts.list', {
    // Using an empty url means that this child state will become active
    // when its parent's url is navigated to. Urls of child states are
    // automatically appended to the urls of their parent. So this state's
    // url is '/contacts' (because '/contacts' + '').
    url: '',
    // IMPORTANT: Now we have a state that is not a top level state. Its
    // template will be inserted into the ui-view within this state's
    // parent's template; so the ui-view within contacts.html. This is the
    // most important thing to remember about templates.
    templateUrl: 'app/contacts/contacts.list.html'
})

the Detail state:

///////////////////////
// Contacts > Detail //
///////////////////////
// You can have unlimited children within a state. Here is a second child
// state within the 'contacts' parent state.
.state('contacts.detail', {
    // Urls can have parameters. They can be specified like :param or {param}.
    // If {} is used, then you can also specify a regex pattern that the param
    // must match. The regex is written after a colon (:). Note: Don't use capture
    // groups in your regex patterns, because the whole regex is wrapped again
    // behind the scenes. Our pattern below will only match numbers with a length
    // between 1 and 4.
    // Since this state is also a child of 'contacts' its url is appended as well.
    // So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the
    // url becomes something like '/contacts/42' then this state becomes active
    // and the $stateParams object becomes { contactId: 42 }.
    url: '/{contactId:[0-9]{1,4}}',
    // If there is more than a single ui-view in the parent template, or you would
    // like to target a ui-view from even higher up the state tree, you can use the
    // views object to configure multiple views. Each view can get its own template,
    // controller, and resolve data.
    // View names can be relative or absolute. Relative view names do not use an '@'
    // symbol. They always refer to views within this state's parent template.
    // Absolute view names use a '@' symbol to distinguish the view and the state.
    // So 'foo@bar' means the ui-view named 'foo' within the 'bar' state's template.
    views: {
        // So this one is targeting the unnamed view within the parent state's template.
        '': {
            templateUrl: 'app/contacts/contacts.detail.html',
            controller: ['$scope', '$stateParams', 'utils',
            function ( $scope, $stateParams, utils) {
                $scope.contact = utils.findById($scope.contacts, $stateParams.contactId);
            }]
        },
        // This one is targeting the ui-view="hint" within the unnamed root, aka index.html.
        // This shows off how you could populate *any* view within *any* ancestor state.
        'hint@': {
            template: 'This is contacts.detail populating the "hint" ui-view'
        },
        // This one is targeting the ui-view="menu" within the parent state's template.
        'menuTip': {
            // templateProvider is the final method for supplying a template.
            // There is: template, templateUrl, and templateProvider.
            templateProvider: ['$stateParams',
            function ( $stateParams) {
                // This is just to demonstrate that $stateParams injection works for templateProvider.
                // $stateParams are the parameters for the new state we're transitioning to, even
                // though the global '$stateParams' has not been updated yet.
                return '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
            }]
        }
    }
})

Summary: In these scenarios, do use the parent/child state definition, because the parent will be loaded only once, and keep its data, while we are iterating among its children

Check these links for some more details:

  • Angular UI Router Nested State resolve in child states
  • why $routeChangeSuccess never gets called?
  • How do I prevent reload on named view, when state changes? AngularJS UI-Router
like image 123
Radim Köhler Avatar answered Nov 13 '22 02:11

Radim Köhler