From e1f455ba4def97d3fc76b53772867b5f9daf4232 Mon Sep 17 00:00:00 2001 From: ncuillery Date: Fri, 2 May 2014 00:00:15 +0200 Subject: [PATCH] fix(ncyBreadcrumb): display the correct breadcrumb in case of direct access When the breadcrumb relies on nested views, the $viewContentLoaded event used by the directive occurs many times. The event related to the deepest view (the correct one for the breadcrumb) is not necessary the last one. We use the '$id' property to get the 'lastly created' scope. Closes #10 --- dist/angular-breadcrumb.js | 31 +++++++++++++++------- dist/angular-breadcrumb.min.js | 2 +- src/angular-breadcrumb.js | 33 +++++++++++++++-------- test/spec/scope-compare-test.js | 47 +++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 test/spec/scope-compare-test.js diff --git a/dist/angular-breadcrumb.js b/dist/angular-breadcrumb.js index 5099fa4..0f488d1 100644 --- a/dist/angular-breadcrumb.js +++ b/dist/angular-breadcrumb.js @@ -3,6 +3,14 @@ * Copyright (c) 2014 Nicolas Cuillery; Licensed MIT */ (function (window, angular, undefined) { +function isAOlderThanB(scopeA, scopeB) { + if(angular.equals(scopeA.length, scopeB.length)) { + return scopeA > scopeB; + } else { + return scopeA.length > scopeB.length; + } +} + function $Breadcrumb() { var $$options = { @@ -149,16 +157,21 @@ function BreadcrumbDirective($interpolate, $breadcrumb, $rootScope) { templateUrl: $breadcrumb.getTemplateUrl(), link: { post: function postLink(scope) { + var lastScopeId; $rootScope.$on('$viewContentLoaded', function (event) { - scope.steps = $breadcrumb.getStatesChain(); - angular.forEach(scope.steps, function (value) { - if (value.data && value.data.ncyBreadcrumbLabel) { - var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel); - value.ncyBreadcrumbLabel = parseLabel(event.targetScope); - } else { - value.ncyBreadcrumbLabel = value.name; - } - }); + // With nested views, the event occur several times, in "wrong" order + if(!lastScopeId || isAOlderThanB(event.targetScope.$id, lastScopeId)) { + lastScopeId = event.targetScope.$id; + scope.steps = $breadcrumb.getStatesChain(); + angular.forEach(scope.steps, function (value) { + if (value.data && value.data.ncyBreadcrumbLabel) { + var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel); + value.ncyBreadcrumbLabel = parseLabel(event.targetScope); + } else { + value.ncyBreadcrumbLabel = value.name; + } + }); + } }); } } diff --git a/dist/angular-breadcrumb.min.js b/dist/angular-breadcrumb.min.js index 430f3a7..617575a 100644 --- a/dist/angular-breadcrumb.min.js +++ b/dist/angular-breadcrumb.min.js @@ -1,4 +1,4 @@ /*! angular-breadcrumb - v0.1.0 - 2014-05-01 * https://github.com/ncuillery/angular-breadcrumb * Copyright (c) 2014 Nicolas Cuillery; Licensed MIT */ -!function(a,b){function c(){var a={prefixStateName:null,template:"bootstrap3",templateUrl:null};this.setOptions=function(c){b.extend(a,c)},this.$get=["$state",function(c){var d=function(a,c){var d=e(a);return b.isDefined(d.data)&&b.isDefined(d.data[c])&&b.equals(a.data[c],d.data[c])},e=function(a){if(b.isDefined(a.parent))return c.get(a.parent);var d=/^(.+)\.[^.]+$/.exec(a.name);return d?c.get(d[1]):null},f=function(a,c,d){var e=!1;return b.forEach(a,function(a){!e&&b.equals(a,c)&&(e=!0)}),e||c.abstract?!1:(d?a.splice(1,0,c):a.unshift(c),!0)},g=function(a){return b.isDefined(a.data)&&b.isDefined(a.data.ncyBreadcrumbParent)&&!d(a,"ncyBreadcrumbParent")?c.get(a.data.ncyBreadcrumbParent):e(a)};return{getTemplate:function(b){return a.templateUrl?null:b[a.template]?b[a.template]:a.template},getTemplateUrl:function(){return a.templateUrl},getStatesChain:function(){var d=[],e=!1;if(a.prefixStateName){var h=c.get(a.prefixStateName);if(!h)throw'Bad configuration : prefixState "'+a.prefixStateName+'" unknown';var i=b.extend(h,{ncyBreadcrumbLink:c.href(h)});e=f(d,i,e)}var j=c.$current.self;do{var k=b.extend(j,{ncyBreadcrumbLink:c.href(j.name)});f(d,k,e),j=g(j)}while(j&&""!==j.name);return d}}}]}function d(a,c,d){return this.$$templates={bootstrap2:'',bootstrap3:''},{restrict:"AE",replace:!0,scope:{},template:c.getTemplate(this.$$templates),templateUrl:c.getTemplateUrl(),link:{post:function(e){d.$on("$viewContentLoaded",function(d){e.steps=c.getStatesChain(),b.forEach(e.steps,function(b){if(b.data&&b.data.ncyBreadcrumbLabel){var c=a(b.data.ncyBreadcrumbLabel);b.ncyBreadcrumbLabel=c(d.targetScope)}else b.ncyBreadcrumbLabel=b.name})})}}}}d.$inject=["$interpolate","$breadcrumb","$rootScope"],b.module("ncy-angular-breadcrumb",["ui.router.state"]).provider("$breadcrumb",c).directive("ncyBreadcrumb",d)}(window,window.angular); \ No newline at end of file +!function(a,b){function c(a,c){return b.equals(a.length,c.length)?a>c:a.length>c.length}function d(){var a={prefixStateName:null,template:"bootstrap3",templateUrl:null};this.setOptions=function(c){b.extend(a,c)},this.$get=["$state",function(c){var d=function(a,c){var d=e(a);return b.isDefined(d.data)&&b.isDefined(d.data[c])&&b.equals(a.data[c],d.data[c])},e=function(a){if(b.isDefined(a.parent))return c.get(a.parent);var d=/^(.+)\.[^.]+$/.exec(a.name);return d?c.get(d[1]):null},f=function(a,c,d){var e=!1;return b.forEach(a,function(a){!e&&b.equals(a,c)&&(e=!0)}),e||c.abstract?!1:(d?a.splice(1,0,c):a.unshift(c),!0)},g=function(a){return b.isDefined(a.data)&&b.isDefined(a.data.ncyBreadcrumbParent)&&!d(a,"ncyBreadcrumbParent")?c.get(a.data.ncyBreadcrumbParent):e(a)};return{getTemplate:function(b){return a.templateUrl?null:b[a.template]?b[a.template]:a.template},getTemplateUrl:function(){return a.templateUrl},getStatesChain:function(){var d=[],e=!1;if(a.prefixStateName){var h=c.get(a.prefixStateName);if(!h)throw'Bad configuration : prefixState "'+a.prefixStateName+'" unknown';var i=b.extend(h,{ncyBreadcrumbLink:c.href(h)});e=f(d,i,e)}var j=c.$current.self;do{var k=b.extend(j,{ncyBreadcrumbLink:c.href(j.name)});f(d,k,e),j=g(j)}while(j&&""!==j.name);return d}}}]}function e(a,d,e){return this.$$templates={bootstrap2:'',bootstrap3:''},{restrict:"AE",replace:!0,scope:{},template:d.getTemplate(this.$$templates),templateUrl:d.getTemplateUrl(),link:{post:function(f){var g;e.$on("$viewContentLoaded",function(e){(!g||c(e.targetScope.$id,g))&&(g=e.targetScope.$id,f.steps=d.getStatesChain(),b.forEach(f.steps,function(b){if(b.data&&b.data.ncyBreadcrumbLabel){var c=a(b.data.ncyBreadcrumbLabel);b.ncyBreadcrumbLabel=c(e.targetScope)}else b.ncyBreadcrumbLabel=b.name}))})}}}}e.$inject=["$interpolate","$breadcrumb","$rootScope"],b.module("ncy-angular-breadcrumb",["ui.router.state"]).provider("$breadcrumb",d).directive("ncyBreadcrumb",e)}(window,window.angular); \ No newline at end of file diff --git a/src/angular-breadcrumb.js b/src/angular-breadcrumb.js index e5c842d..f3f7139 100644 --- a/src/angular-breadcrumb.js +++ b/src/angular-breadcrumb.js @@ -1,3 +1,11 @@ +function isAOlderThanB(scopeA, scopeB) { + if(angular.equals(scopeA.length, scopeB.length)) { + return scopeA > scopeB; + } else { + return scopeA.length > scopeB.length; + } +} + function $Breadcrumb() { var $$options = { @@ -112,7 +120,6 @@ function $Breadcrumb() { } }; }]; - } function BreadcrumbDirective($interpolate, $breadcrumb, $rootScope) { @@ -144,16 +151,21 @@ function BreadcrumbDirective($interpolate, $breadcrumb, $rootScope) { templateUrl: $breadcrumb.getTemplateUrl(), link: { post: function postLink(scope) { + var lastScopeId; $rootScope.$on('$viewContentLoaded', function (event) { - scope.steps = $breadcrumb.getStatesChain(); - angular.forEach(scope.steps, function (value) { - if (value.data && value.data.ncyBreadcrumbLabel) { - var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel); - value.ncyBreadcrumbLabel = parseLabel(event.targetScope); - } else { - value.ncyBreadcrumbLabel = value.name; - } - }); + // With nested views, the event occur several times, in "wrong" order + if(!lastScopeId || isAOlderThanB(event.targetScope.$id, lastScopeId)) { + lastScopeId = event.targetScope.$id; + scope.steps = $breadcrumb.getStatesChain(); + angular.forEach(scope.steps, function (value) { + if (value.data && value.data.ncyBreadcrumbLabel) { + var parseLabel = $interpolate(value.data.ncyBreadcrumbLabel); + value.ncyBreadcrumbLabel = parseLabel(event.targetScope); + } else { + value.ncyBreadcrumbLabel = value.name; + } + }); + } }); } } @@ -164,4 +176,3 @@ BreadcrumbDirective.$inject = ['$interpolate', '$breadcrumb', '$rootScope']; angular.module('ncy-angular-breadcrumb', ['ui.router.state']) .provider('$breadcrumb', $Breadcrumb) .directive('ncyBreadcrumb', BreadcrumbDirective); - diff --git a/test/spec/scope-compare-test.js b/test/spec/scope-compare-test.js new file mode 100644 index 0000000..0c87f83 --- /dev/null +++ b/test/spec/scope-compare-test.js @@ -0,0 +1,47 @@ +/*jshint undef: false */ + +describe('The scope', function() { + + beforeEach(function() { + module('ncy-basic-conf'); + }); + + it('00A is older than 001', function() { + expect(isAOlderThanB('00A', '001')).toBe(true); + }); + + it('010 is older than 00Y', function() { + expect(isAOlderThanB('010', '00Y')).toBe(true); + }); + + it('01P is older than 010', function() { + expect(isAOlderThanB('01P', '010')).toBe(true); + }); + + it('FOO is older than BAR', function() { + expect(isAOlderThanB('FOO', 'BAR')).toBe(true); + }); + + it('F00 is older than BAR', function() { + expect(isAOlderThanB('F00', 'BAR')).toBe(true); + }); + + it('0000 is older than ZZZ', function() { + expect(isAOlderThanB('0000', 'ZZZ')).toBe(true); + }); + + it('(newly created) is always older than the precedent one', inject(function($rootScope) { + var scope = $rootScope.$new(); + for(var i = 0; i < 100000; i++) { + var newScope = $rootScope.$new(); + var isOlder = isAOlderThanB(newScope.$id, scope.$id); + expect(isOlder).toBe(true); + if(!isOlder) { + console.log(newScope.$id, scope.$id, isOlder, i); + break; + } + scope = newScope; + } + })); + +});