ncuillery / angular-breadcrumb

Generate a breadcrumb from ui-router's states
http://ncuillery.github.io/angular-breadcrumb/
MIT License
785 stars 183 forks source link

Route Parameters Are Lost #26

Closed elalchemist closed 10 years ago

elalchemist commented 10 years ago

Hello,

Excellent plugin!

However, I cannot use the breadcrumb with routes that have parameters. When I click on a breadcrumb the route has no parameter in it. For example, what was before 'app/levels/1' simply becomes 'app/levels/'. How do I get the breadcrumb to keep the route parameters?

ncuillery commented 10 years ago

Hi,

Normally, the module keeps the route parameters (see here: the link of state "Room 102" is correct). Please can you give me more information by posting your $stateProvider configuration here ?

elalchemist commented 10 years ago

Wow! That was prompt! As you requested, here's my route configuration:

// Setting up route
angular.module('progress').config(['$stateProvider',
    function($stateProvider) {
        $stateProvider.
        state('app.levels', {
            url: '/levels',
            templateUrl: 'modules/progress/views/progress.client.view.html',
            controller: 'LevelsController',
            data: {
                ncyBreadcrumbLabel: 'ALTA Online'        
            }    
        }).
        state('app.books', {
            url: '/books/{levelId}',
            templateUrl: 'modules/progress/views/progress.client.view.html',
            controller: 'BooksController',
            data: {
                ncyBreadcrumbLabel: '{{trail.level}}',                
                ncyBreadcrumbParent: 'app.levels'
            }
        }).
        state('app.lessons', {
            url: '/lessons/{bookId}',
            templateUrl: 'modules/progress/views/progress.client.view.html',
            controller: 'LessonsController',
            data: {
                ncyBreadcrumbLabel: '{{trail.book}}',               
                ncyBreadcrumbParent: 'app.books'
            }
        }).state('app.activities', {
            url: '/activities/{lessonId}',
            templateUrl: 'modules/progress/views/progress.client.view.html',
            controller: 'ActivitiesController',
            data: {
                ncyBreadcrumbLabel: 'LESSON: {{trail.lesson}}',
                ncyBreadcrumbParent: 'app.lessons'
            }
        });
    }
]);
ncuillery commented 10 years ago

I have done some investigations about your configuration in this plkr. It is not exactly the problem you describe but I think the cause is the same:

You have a hierarchy in your app (if I understand well: level > book > lesson > activity) but your states are sibling. So a state can't inherit data from its ancestors (there is no ancestor).

You need to defined a real hierarchy of states by doing this:

app.levels
app.levels.books
app.levels.books.lessons
app.levels.books.lessons.activities

It will produce these urls:

/levels/{levelId}
/levels/{levelId}/books/{bookId}
/levels/{levelId}/books/{bookId}/lessons/{lessonId}
/levels/{levelId}/books/{bookId}/lessons/{lessonId}/activities

As far as I understand your context, it seems more legit to me.

Define a ncyBreadcrumbParent allows to customize the breadcrumb lightly but doesn't load the parent on behalf of the ui-router.

With your configuration, the breadcrumb can't work because if you access to activities, it needs to know in which level, in which book and in which lesson you are. I think it's the same in the rest of your application: you can navigate well through the progress screen by keeping the trail shared object in memory, but what happened if you press F5 ? Your app would have the same problem.

elalchemist commented 10 years ago

Hey!

Thanks a lot. You really clarified much for me. I got it to work using the configuration you suggested. This issue is therefore officially closed. However, I would like to request your advice on another issue which popped up:

I want child views to completely replace their parent views. Therefore I configured my app like so:

code

And so forth.

It works fine but when I check the html on the browser source I notice a series of nested

elements. I just want to know if this is good practice or if there is another way for a child view to completely override the contents of its parent view.

Thanks much.

coltonmccormack commented 10 years ago

@elalchemist,

All you have to do is explicitly tell your child view to use the ui-view of it's ancestor's state in your $state config by using absolute naming, rather than let it just assume you want to nest the view in an unnamed ui-view of the immediate parent's template. All the info you need is here: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views.

So your states will look something like this (although technically you could probably leave the whole app.levels state unchanged from the original code):

// Setting up route
angular.module('progress').config(['$stateProvider',
   function($stateProvider) {
       $stateProvider.
       state('levels', {
           url: '/levels',
           parent: 'app',
           data: {
               ncyBreadcrumbLabel: 'ALTA Online'
           },
           views:{
               '@app': {
                   templateUrl: 'modules/progress/views/progress.client.view.html',
                   controller: 'LevelsController'
               }
           }
       }).
       state('books', {
           url: '/books/{levelId}',
           parent: 'levels',
           templateUrl: 'modules/progress/views/progress.client.view.html',
           controller: 'BooksController',
           data: {
               ncyBreadcrumbLabel: '{{trail.level}}'
           },
           views:{
               '@app': {
                   templateUrl: 'modules/progress/views/progress.client.view.html',
                   controller: 'LevelsController'
               }
           }
       }).
       state('lessons', {
           url: '/lessons/{bookId}',
           parent: 'books',
           templateUrl: 'modules/progress/views/progress.client.view.html',
           controller: 'LessonsController',
           data: {
               ncyBreadcrumbLabel: '{{trail.book}}'
           },
           views:{
               '@app': {
                   templateUrl: 'modules/progress/views/progress.client.view.html',
                   controller: 'LevelsController'
               }
           }
       }).state('activities', {
           url: '/activities/{lessonId}',
           parent: 'lessons',
           data: {
               ncyBreadcrumbLabel: 'LESSON: {{trail.lesson}}'
           },
           views:{
               '@app': {
                   templateUrl: 'modules/progress/views/progress.client.view.html',
                   controller: 'ActivitiesController'
               }
           }
       });
   }
]);

Warning, this code is untested and might have probably has errors, but it should get you on the right path :)

elalchemist commented 10 years ago

Hello,

Thank you. It seemed to work fine as the views are rendered appropriately. However this new configuration messes up my breadcrumb. Now only two breadcrumb labels are shown; the first and the current. The labels in between those two are blank. I am not using the trail object by the way. I use the parameter from the route to query the database for the relevant title.

Any advice?

coltonmccormack commented 10 years ago

Just make sure that whatever breadcrumb label you want evaluates within the current $scope. So if you are viewing a lesson and want the book title to show then make sure that (at least in my example) the $scope.trail.book value exists. So something like this would work:

$scope.trail.book = getTitle($stateParams.bookId);

assuming that your function to get a book title from the database with the current parameter is called getTitle() of course, you should replace $scope.trail.book and getTitle() with whatever you actually use.

elalchemist commented 10 years ago

Hey thanks. I found the root problem after considering what the new configuration meant for the scopes; since the views are now injected into the ui-view container of the 'app' state's view, the scope of the 'app' state view is the parent scope of all my nested views regardless of where they fall in the hierarchy. Hence all I had to do was set my titles to the parent scope that they would persist on the breadcrumb as I went further down the hierarchy. For example:

In the LessonsController I set the book title like so:

$scope.$parent.book = book.title(id); //book is the service I use to get book title from the backened. //'id' is taken from the state's route parameters

Similarly, in the BooksController I set the level title like so:

$scope.$parent.level = level.title(id);

In each instance the respective title is set on the 'app' state's scope. Hence when I specify in my state configuration that I wish to bind the breadcrumb label to a particular scope item (level, book etc), it drills up to the parent scope and finds it.

I am not sure if my assumptions here are valid. Still I have learnt so much from troubleshooting this problem. I am immensely grateful for all of you who assisted. I consider this problem officially closed but feel free to add further clarification.

Thank you.

vchandrol commented 8 years ago

Hi @ncuillery if we have search box and if we enter some text and that folder/file exist in ui route than how can we show search path in breadcrumb and open that path as well...could you please provide solution for this.