Git Product home page Git Product logo

Comments (12)

enkodellc avatar enkodellc commented on July 17, 2024

I did come up with a workaround... but it will only work for my situation since I plan on using my abstract states for each module in a similar fashion.

                    if (displayName !== false) {                       
                        var myRoute = currentState.abstract ? currentState.name + '.all ' + currentState.name : currentState.name;

                        breadcrumbs.push({
                            displayName: displayName,
                            route: myRoute
                        });
                    }

from angularutils.

michaelbromley avatar michaelbromley commented on July 17, 2024

Hi,

Good point - I didn't account for abstract states. Thanks for posting your work around, and I'll think about how a general solution could be implemented. If you come up with anything more general, feel free to post it or even do a pull request.

from angularutils.

michaelbromley avatar michaelbromley commented on July 17, 2024

Idea: Your solution involves re-assigning any breadcrumb of an abstract state to an alternative non-abstract (concrete?) state. In your case, you have a particular convention that makes it work out for you.

More generally, we could adapt that concept but make the redirection user-definable in the $state object. Something along the lines of this:

$stateProvider
            .state('articles', {
                url: '',
                abstract: true,
                template: '<ui-view/>',
                resolve: {
                    loggedin: checkLoggedin
                },
                data: {
                    breadcrumbsProxy: 'articles.all'
                }
            })
            .state('articles.all articles', {
                url: '/articles',
                templateUrl: 'articles/views/list.html',
                data: {
                    displayName: 'Articles'
                }
            })

So we put a user-defined property called (for example, off the top of my head) breadcrumbsProxy, which tells the directive which state to substitute for the abstract one. The name of the property could be specified as an attribute, much in the same way as is currently done with the displayname-property.

When I get a bit of time I'll try to implement something along those lines.

from angularutils.

enkodellc avatar enkodellc commented on July 17, 2024

Michael that sounds like a great plan. I might have some time today to try and implement and test it with my project.

from angularutils.

enkodellc avatar enkodellc commented on July 17, 2024

Micheel, I am new to Angular and am having difficulty understanding how the code is retrieving the displayname property from the data object. I am trying to replicate that functionality for the breadcrumbProxy but am failing miserably. Can you point me in the right direction on the methodology or angularjs functionality to obtain the displayname from the data object in ui-router.

from angularutils.

michaelbromley avatar michaelbromley commented on July 17, 2024

Reading over the code myself, I even had to take a minute to figure out what was going on, so don't feel bad about not getting it!

Here's an explanation of what's going on:

https://github.com/michaelbromley/angularUtils/blob/master/src/directives/uiBreadcrumbs/uiBreadcrumbs.js#L53-L82

function getDisplayName(currentState) {
    var i;
    var propertyReference;
    var propertyArray;
    var displayName;

    if (!scope.displaynameProperty) {
        // if the displayname-property attribute was not specified, default to the state's name
        return currentState.name;
    }
    propertyArray = scope.displaynameProperty.split('.');
    propertyReference = currentState;

    for (i = 0; i < propertyArray.length; i ++) {
        if (angular.isDefined(propertyReference[propertyArray[i]])) {
            if (propertyReference[propertyArray[i]] === false) {
                return false;
            } else {
                propertyReference = propertyReference[propertyArray[i]];
            }
        } else {
            // if the specified property was not foundm default to the state's name
            return currentState.name;
        }
    }
    // use the $interpolate service to handle any bindings in the propertyReference string.
    displayName = $interpolate(propertyReference)(currentState.locals.globals);

    return displayName;
}

Let's assume the following setup:

.state('articles.article by id', {
    url: '/articles/:articleId',
    templateUrl: 'articles/views/view.html',
    data: {
        displayName: '{{ article.title }}'
        }
})
<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>
  1. So, the scope.displaynameProperty comes from our attribute on the element, and is created on the scope for us by the scope: {displaynameProperty: '@'} in our directive definition object.
  2. We then split it into an array by ".", so in out case we get an array ['data', 'displayName'].
  3. The for``loop then traverses down the current$statedefinition object, checking if firstly the "data" property is defined on it. In our example the answer is "yes", and the value of it would be: { displayName : '{{ article.title }}' }`. We then set that result as the object to check in the next iteration.
  4. In the next iteration of the loop, we check whether the next property from the array (displayName) is defined on the new object obtained from the first iteration. It is, and its value is the string `'{{ article.title }}'.
  5. Now the loop is done, and we have successfully obtained the title for the breadcrumb. It could be a simple string, boolean false, or in this case, a special string in the syntax that Angular can iterpolate to find a dynamic value.
  6. To turn the string '{{ article.title }} into the actual value of the object article.title, we need to use the Angular service $interpolate. That service takes an Angular expression which we want to interpolate ({{ article.title }}), and returns a function that then takes a context that is used to look up the value. By "context" is meant basically an object which is used to loop up the value to put into the {{ expression }}.
  7. In our case, that context is currentState.locals.globals - which is basically a pointer to resolve property on our config object. So this allows it to look in that object, find the article object, and return the value of its property title.

I hope I made that easy to follow. If you implement something similar, but for the "beadcrumbsProxy" idea, I'd suggest that a bunch of the logic from the above function could be shared by the new function to avoid repetition. Good luck!

from angularutils.

michaelbromley avatar michaelbromley commented on July 17, 2024

I implemented the fix suggested above - see e7adb82 (plus a few subsequent tweaks)

See the updated docs: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/uiBreadcrumbs#working-with-abstract-states

The Plunker demo is also updated to demonstrate abstract states: http://plnkr.co/edit/bBgdxgB91Z6323HLWCzF?p=preview

from angularutils.

enkodellc avatar enkodellc commented on July 17, 2024

I got around to implementing your updates. I simplified the updateBreadcrumbsArray() a bit to get the expected output I wanted. I am not concerned about duplicate states and your demo doesn't seem to include the abstract state breadcrumb? Here is my function that I will be using. Thanks for your updates.


            /**
             * Start with the current state and traverse up the path to build the
             * array of breadcrumbs that can be used in an ng-repeat in the template.
             */
            function updateBreadcrumbsArray() {
                var breadcrumbs = [];
                var currentState = $state.$current;

                while(currentState && currentState.name !== '') {
                    var workingState = getWorkingState(currentState);
                    var displayName = getDisplayName(currentState);

                    if (displayName !== false) {
                        breadcrumbs.push({
                            displayName: displayName,
                            route: workingState.name
                        });
                    }
                    currentState = currentState.parent;
                }

                breadcrumbs.reverse();
                scope.breadcrumbs = breadcrumbs;
            }

from angularutils.

michaelbromley avatar michaelbromley commented on July 17, 2024

Hi, okay - looks good. In the Plunker demo, the 'home.users' state is abstract, and it delegates to the 'home.user.list' state. Thus, when you are in the 'home.users.detail' state, clicking on 'Users' in the breadcrumbs will take you to 'users.list'.

from angularutils.

enkodellc avatar enkodellc commented on July 17, 2024

@michaelbromley sorry, yours works great, I was mistaken. Awesome job.

from angularutils.

dannygoncalves avatar dannygoncalves commented on July 17, 2024

Sorry for resurrecting this old thread, how can I do if I want to show the abstract state (done) but remove the href from it? making it plain text and non-clickable?

from angularutils.

enkodellc avatar enkodellc commented on July 17, 2024

@dannygoncalves Do do this quickly I would for the uibreadcrumb and create either a custom property or do a simple trick on the displayname.

For instance let's say you wanted to have your "Todo" breadcrumb to be only a link then instead of displayName: 'Todo' make it displayName: '[-]Todo' then update the following functions:


        function stateAlreadyInBreadcrumbs(state, breadcrumbs) {
          var i;
          var alreadyUsed = false;
          for(i = 0; i < breadcrumbs.length; i++) {
//check if route exists
             if (breadcrumbs[i].route && (breadcrumbs[i].route === state.name)) {
               alreadyUsed = true;
             }
          }
          return alreadyUsed;
        }

Inside updateBreadcrumbsArray..

             //custom logic to not add the route and so it is text only
             if (displayName.indexOf('[-]') !== -1) {
                breadcrumbs.push({
                  displayName: displayName.replace('[-]','')
                });
              } else {
                breadcrumbs.push({
                  displayName: displayName,
                  route: workingState.name
                });
              }

I tested this out and it works. You could add an additional property like textonlyProperty: '@' and check for that before you push the breadcrumb.

from angularutils.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.