UI Router load detail page from a list page

All we need is an easy explanation of the problem, so here it is.

AngularJS app using ui-router.
My list page loads correctly, but when clicking on links on the list page my url changes but my html on the page does not change, it remains on the list page. What is wrong with this routing?

app.js

var myApp = angular.module('myApp', ['ui.router']);

myApp.config([
    '$stateProvider', function($stateProvider) {
        $stateProvider
            .state('products', {
                url: '',
                templateUrl: 'Scripts/templates/manageProducts/products.list.html',
                controller: 'productListCtrl'
            })
            .state('products.detail', {
                url: '/:id',
                templateUrl: 'Scripts/templates/manageProducts/products.detail.html',
                controller: 'productDetailCtrl'
            });
    }
]);

Index.html

<div ng-app="myApp">
    <div ui-view></div>
</div>

On the products.list.html template:

<a ui-sref="products.detail({ id: 1 })">Detail for Item 1</a>

Should I even be using UI Router? The list and details page are 2 distinct screens.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

There is an plunker, which should help to give an answer:

Should I even be using UI Router? The list and details page are 2 distinct screens.

In case, that we would continue with productDetails state, we do loose something (if not even a lot).

In the example we can see this state definition:

$stateProvider

    // parent state for products.detail
    // the important thing here is that it must contain
    // ui-view="details", because the child is targeting it
    .state('products', {
      url: '/products',
      templateUrl: 'products.list.html',
      controller: 'productListCtrl'
    })
    // here, we will hook into the parent ui-view
    // that means one essential thing:
    // our scope, will be inherited from parent
    .state('products.detail', {
      url: '^/:id',
      views: {
        'detail': {
          templateUrl: 'products.detail.html',
          controller: 'productDetailCtrl'
        }
      },
    })

Until now we’ve seen the standard nested states parent/child. Next we will define the sub-state, while targeting the root ui-view=""

    // this one is as the productDetails
    // it skips parent and targets the root view
    // despite of the fact, that it is defined as sub-state of the products !
    // we won't get anything from parent state
    .state('products.detailAsRoot', {
      url: '^/product/:id',
      views: {
        '@': {
          templateUrl: 'products.detail.html',
          controller: 'productAsRootCtrl'
        }
      },
    });

Firstly, the inheritance in javascript/scopes is tremendously explained here:

And also, important is, that scopes in ui-router are inherited in a way of “view nesting”

A fundamental cite:

Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).

So what is all this answer about? To say: if we will use ui-router, the biggest benefit is the scope inheritance. Parent can do something once… child(ren) can just reuse it.

Also see:

Method 2

I had to make the details page it’s own state, as follows:

        .state('productDetails', {
            url: '/:id',
            templateUrl: 'Scripts/templates/manageProducts/products.detail.html',
            controller: 'productDetailCtrl'
        })

instead of ‘product.details’ I used ‘productDetails’

Method 3

Using a root abstract state:

.state('products', {
  abstract: true,
  url: '/products',
  template: '<ui-view/>'
})
.state('products.list', {
  url: '',
  template: '<div>Products list</div>'
})
.state('products.detail', {
  url: '/:productId',
  template: '<div>Product detail</div>'
}) 

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply