AngularJS: Multiple views with routing without losing scope
Asked Answered
P

4

12

I'm trying to implement a classic list/details UI. When clicking an item in the list, I want to display an edit form for that item while still displaying the list. I'm trying to work around Angular's 1-view-per-page limitation and decided to do it by having all URLs routed to the same controller/view. (Perhaps this is the root of my problem and I'm open to alternatives.)

Routing:

$routeProvider
    .when('/list', { templateUrl: '/Partials/Users.html', controller: UserController })
    .when('/edit/:UserId', { templateUrl: '/Partials/Users.html', controller: UserController })
    .otherwise({ redirectTo: '/list' });

The view (/Partials/Users.html):

<!-- List of users -->
<div ng-repeat="user in Users">
    <a href="*/edit/{{ user.Id }}">Edit {{ user.Name }}</a>
</div>

<!-- Edit form -->
<div>
    {{ SelectedUser.Name }}
</div>

Controller:

function UserController($scope, $routeParams) {
    // the model for the list
    $scope.Users = GetUserListFromService();

    // the model for the edit form
    if ($routeParams.UserId != null)
        $scope.SelectedUser = GetUserFromService($routeParams.UserId);
}

Problems:

  1. When clicking an edit link, the controller is reinstantiated with a new scope, so I have to re-init the Users list. (In a more complex example I could have input from the user stored bound to the model and this would also get lost.) I'd prefer to persist the scope from the previous route.
  2. I'd prefer to use a separate controller (or, as many other Angular developers have complained, the ability to have multiple displayed views!) but that leads to the same issue of losing scope.
Pitch answered 18/4, 2013 at 14:15 Comment(1)
possible duplicate of Angularjs, passing scope between routesPennant
M
14

Try using ui-router: http://github.com/angular-ui/ui-router.

They have nested views and easier state management than angular default routing :-)

Morsel answered 18/4, 2013 at 14:59 Comment(3)
Thanks! It wasn't easy to follow their tutorial and samples, so for others trying to implement the same pattern I suggest you create a "state" for each view and use nested states to allow scope to be shared.Pitch
I'd like to see how you did this. I'm having trouble going the same route with this choice of router.Rachaba
@timothy github.com/dotJEM/angular-routing is an alternative to the UI-Router which has a simple aproach to views (no magic stuff with views@views etc... just pure and simple naming of all views)... If that was the cause of your trouble (the UI-Router view model has received quite a bit of critique, and many seems to have a hard time using it beyond the very simple cases)Gherardo
F
7

Multiple views are not supported in core AngularJS. You can use this library for this purpose which supports any amount of nested views on the page, where each level is configured independently with its own controller and template:

http://angular-route-segment.com

It is much simpler to use than ui-router. Sample config may look like this:

$routeSegmentProvider.

when('/section1',          's1.home').
when('/section1/prefs',    's1.prefs').
when('/section1/:id',      's1.itemInfo.overview').
when('/section1/:id/edit', 's1.itemInfo.edit').
when('/section2',          's2').

segment('s1', {
    templateUrl: 'templates/section1.html',
    controller: MainCtrl}).

within().

    segment('home', {
        templateUrl: 'templates/section1/home.html'}).

    segment('itemInfo', {
        templateUrl: 'templates/section1/item.html',
        controller: Section1ItemCtrl,
        dependencies: ['id']}).

    within().

        segment('overview', {
            templateUrl: 'templates/section1/item/overview.html'}).

        segment('edit', {
             templateUrl: 'templates/section1/item/edit.html'}).

        up().

    segment('prefs', {
        templateUrl: 'templates/section1/prefs.html'}).

    up().

segment('s2', {
    templateUrl: 'templates/section2.html',
    controller: MainCtrl});
Fissure answered 22/8, 2013 at 18:25 Comment(0)
P
1

I've found Angular Multi View to be a godsend for this scenario. It lets you preserve scope as the route changes and lets multiple controllers share the same route without nesting your views.

I recommend Angular Multi View if you have more than 2 views on your page. Otherwise, when using ui-router, nesting multiple views gets messy really fast.

Pitch answered 7/8, 2013 at 16:41 Comment(0)
M
0

I came up with the same problem and I personnaly don't like plugins when they aren't absolutely unavoidable. I just moved singleton part to a service.

In my case there are :id[/:mode] routes and I want to react different way if user changes just mode or id too. Thus, I have to know previous id.

So, there is a service with activate method which updates its state. And the scope is reinitialized every time with the following code.

module.controller('MyController', ['$scope', '$routeParams', 'navigator', function($scope, $routeParams, navigator) {
    var id = null;
    var mode = null;

    if (typeof($routeParams.id)!='undefined')
    {
        id = $routeParams.id;
    }

    if (typeof($routeParams.mode)!='undefined')
    {
        mode = $routeParams.mode;
    }

    navigator.activate(id, mode);

    $scope.items = navigator.items;
    $scope.activeItem = navigator.activeItem;
    $scope.modes = navigator.modes;
    $scope.activeMode = navigator.activeMode;
}]);

In activate method I can compare id to the singleton's activeItem.id and react differently.

Mangosteen answered 23/8, 2013 at 11:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.