1

I am creating a master-detail page using the Angular UI router. I have been able to make it work - when I click on a master record, the id of this record is appended to the URL and the detail view shows the detail for this record. However I have two questions:

  1. Is it possible to make this work without a change in the URL? As I understand it the UI router is all about state changes, not necessarily url changes. However, if I remove the url property from the router state, then detail doesn't show up.
  2. The template associated with my detail state is a directive. Currently this directive gets the id of the master record using $stateParams. This makes it dependent on the UI router. Is it possible to pass this id down using isolate scope?

Here's are the key pieces of my code:

The router is configured as follows:

$stateProvider
    .state('orderdetail', {
        url: '/orders/:id',
        template: '<my-order-detail></my-order-detail>'
    });
}

As mentioned in #1 above, if I comment out the url, then detail stops appearing. Why? Is there a way to make this work without having the URL to change?

When the master record is selected I change the state as follows:

$state.go('orderdetail', {
    id: vm.selectedOrderId
});

Here's the code for the directive that shows the detail:

angular.module('app.orderdetail', []);

angular.module('app.orderdetail')
    .directive('myOrderDetail', orderDetailDirective)
    .controller('OrderDetailController', OrderDetailController);

// ----- orderDetailDirective -----
orderDetailDirective.$inject = [];

function orderDetailDirective() {

    var directive = {
        restrict: 'E',
        templateUrl: 'components/orderdetail/orderdetail.html',
        scope: {
            id: '='
        },
        controller: 'OrderDetailController',
        controllerAs: 'vm'
    };

    return directive;
}

// ----- OrderDetailController -----
OrderDetailController.$inject = ['$stateParams'];

/* @ngInject */
function OrderDetailController($stateParams) {
    var vm = this;
    vm.id = $stateParams.id;
}

Note that the controller captures the id of the selected master record using $stateParams. I would love to remove this dependency in the directive by switching to isolate scope. So the template associated with the UI router state should look something like this:

template: '<my-order-detail data-id={{vm.id}}></my-order-detail>'

Is this possible?

John Slegers
  • 45,213
  • 22
  • 199
  • 169
Naresh
  • 23,937
  • 33
  • 132
  • 204
  • Are you looking for [`resolve`](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) ? Resolve allows you to manually hookup injections before calling a controller. `{[...], resolve: function() { return 42} }` – Ross Rogers Apr 02 '15 at 22:30

1 Answers1

0

In case, I do understand your goal correctly, we can have this template, passing the id into directive (and its isolated scope).

There is a working plunker

$stateProvider
    .state('orderdetail', {
      url: '/orders/:id',
      //template: '<my-order-detail></my-order-detail>',
      template: '<my-order-detail data-id="$stateParams.id"></my-order-detail>',
    });

Just added this to our code (so we do not need controller, $stateParams are already in scope of each view state template):

.run(['$rootScope', '$state', '$stateParams',
    function($rootScope, $state, $stateParams) {
      $rootScope.$state = $state;
      $rootScope.$stateParams = $stateParams;
    }
])

And this is the way its called

<a ui-sref="orderdetail({id:1})">
<a ui-sref="orderdetail({id:2})">

Check that in action here

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Thanks Radim, this works, but I am not very thrilled about adding $state and $stateParams to $rootScope. This essentially makes them global. It doesn't look like UI router is very directive friendly. One pretty much has to expose controllers. – Naresh Apr 04 '15 at 22:16
  • Not only is my solution working, but it is the native UI-Router approach! ;) Check it here https://github.com/angular-ui/ui-router/blob/master/sample/app/app.js#L10 you will be delighted ;). In fact, UI-Router views represent a frame, a place where we can place directives. Usually we place more than just one, but **your wish** and *my example* in fact fits into very native way how to use **directive** inside of the **view**. I'd say... enjoy UI-Router anyhow – Radim Köhler Apr 05 '15 at 05:20
  • Radim, thanks for the reference. It gives me more confidence in the approach, so I am marking your answer as correct :-)! As a side note, I was trying to push this approach even further by not just sending the selected orderId to the detail view, but using a resolve to send the full order detail. The way I am doing this is to create an intermediate controller in my router configuration and using that to coordinate with the directive: `controller: ['orderdetail', function(orderdetail) { this.orderdetail = orderdetail; }]` – Naresh Apr 05 '15 at 16:55
  • Great to see that all. UI-Router is really **the tool**. Good luck sir – Radim Köhler Apr 05 '15 at 16:59