Skip to content

Commit

Permalink
feat($breadcrumb): allow chain of states customization
Browse files Browse the repository at this point in the history
The property 'ncyBreadcrumbParent' in states configuration override the parent state (only for the breadcrumb).
It is useful when 2 states are not parent in states configuration but must be parent in the breadcrumb.
For example, in ui-router sample : contact.list and contact.detail are configured as siblings but must be parent/child in breadcrumb :
contact.list > contact.detail

Closes #7
  • Loading branch information
Nicolas Cuillery committed Apr 25, 2014
1 parent c60a3ff commit 028e493
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 43 deletions.
55 changes: 37 additions & 18 deletions dist/angular-breadcrumb.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! angular-breadcrumb - v0.1.0 - 2014-04-24
/*! angular-breadcrumb - v0.1.0 - 2014-04-25
* https://github.com/ncuillery/angular-breadcrumb
* Copyright (c) 2014 Nicolas Cuillery; Licensed MIT */
angular.module('ncy-angular-breadcrumb', ['ui.router.state'])
Expand All @@ -16,39 +16,58 @@ angular.module('ncy-angular-breadcrumb', ['ui.router.state'])

this.$get = ['$state', function($state) {

// Add the state in the array if not already in and if not abstract
var $$addStateInChain = function(array, state, prefixStateInserted) {
var stateAlreadyInArray = false;
angular.forEach(array, function(value) {
if(!stateAlreadyInArray && angular.equals(value, state)) {
stateAlreadyInArray = true;
// Check if a property in state's data is inherited from the parent state
var $$isInherited = function(state, dataProperty) {
var parentState = $$parentState(state);
return angular.isDefined(parentState.data) &&
angular.isDefined(parentState.data[dataProperty]) &&
angular.equals(state.data[dataProperty], parentState.data[dataProperty]);
};

// Get the parent state
var $$parentState = function(state) {
if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
}

var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
}
return null;
};

// Add the state in the chain if not already in and if not abstract
var $$addStateInChain = function(chain, state, prefixStateInserted) {
var stateAlreadyInChain = false;
angular.forEach(chain, function(value) {
if(!stateAlreadyInChain && angular.equals(value, state)) {
stateAlreadyInChain = true;
}
});
if(!stateAlreadyInArray && !state.abstract) {
if(!stateAlreadyInChain && !state.abstract) {
// Insert at first or second index.
if(prefixStateInserted) {
array.splice(1, 0, state);
chain.splice(1, 0, state);
} else {
array.unshift(state);
chain.unshift(state);
}
return true;
}
return false;
};

// Get the parent of a state
// Get the state for the parent step in the breadcrumb
var $$breadcrumbParentState = function(state) {

if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
if(angular.isDefined(state.data) &&
angular.isDefined(state.data.ncyBreadcrumbParent) &&
!$$isInherited(state, 'ncyBreadcrumbParent')) {
return $state.get(state.data.ncyBreadcrumbParent);
}

var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
}
return $$parentState(state);

return null;
};

return {
Expand Down
4 changes: 2 additions & 2 deletions dist/angular-breadcrumb.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 36 additions & 17 deletions src/angular-breadcrumb.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,58 @@ angular.module('ncy-angular-breadcrumb', ['ui.router.state'])

this.$get = ['$state', function($state) {

// Add the state in the array if not already in and if not abstract
var $$addStateInChain = function(array, state, prefixStateInserted) {
var stateAlreadyInArray = false;
angular.forEach(array, function(value) {
if(!stateAlreadyInArray && angular.equals(value, state)) {
stateAlreadyInArray = true;
// Check if a property in state's data is inherited from the parent state
var $$isInherited = function(state, dataProperty) {
var parentState = $$parentState(state);
return angular.isDefined(parentState.data) &&
angular.isDefined(parentState.data[dataProperty]) &&
angular.equals(state.data[dataProperty], parentState.data[dataProperty]);
};

// Get the parent state
var $$parentState = function(state) {
if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
}

var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
}
return null;
};

// Add the state in the chain if not already in and if not abstract
var $$addStateInChain = function(chain, state, prefixStateInserted) {
var stateAlreadyInChain = false;
angular.forEach(chain, function(value) {
if(!stateAlreadyInChain && angular.equals(value, state)) {
stateAlreadyInChain = true;
}
});
if(!stateAlreadyInArray && !state.abstract) {
if(!stateAlreadyInChain && !state.abstract) {
// Insert at first or second index.
if(prefixStateInserted) {
array.splice(1, 0, state);
chain.splice(1, 0, state);
} else {
array.unshift(state);
chain.unshift(state);
}
return true;
}
return false;
};

// Get the parent of a state
// Get the state for the parent step in the breadcrumb
var $$breadcrumbParentState = function(state) {

if (angular.isDefined(state.parent)) {
return $state.get(state.parent);
if(angular.isDefined(state.data) &&
angular.isDefined(state.data.ncyBreadcrumbParent) &&
!$$isInherited(state, 'ncyBreadcrumbParent')) {
return $state.get(state.data.ncyBreadcrumbParent);
}

var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
if(compositeName) {
return $state.get(compositeName[1]);
}
return $$parentState(state);

return null;
};

return {
Expand Down
3 changes: 3 additions & 0 deletions test/mock/test-ui-router-sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ angular.module('ncy-ui-router-conf', ['ngMock'])
return '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
}]
}
},
data: {
ncyBreadcrumbParent: 'contacts.list' // Override the parent state (only for the breadcrumb).
}
})
.state('contacts.detail.item', {
Expand Down
12 changes: 6 additions & 6 deletions test/spec/service-ui-router-sample-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ describe('Service with ui-router\'s sample conf', function() {
expect(stringifyStateChain(statesChain)).toBe('home --> contacts.list');
}));

it('generate two steps for the "contacts.detail" state', inject(function($breadcrumb) {
it('generate three steps for the "contacts.detail" state', inject(function($breadcrumb) {
goToStateAndFlush('contacts.detail', {contactId: 42});
var statesChain = $breadcrumb.getStatesChain();

expect(stringifyStateChain(statesChain)).toBe('home --> contacts.detail');
expect(stringifyStateChain(statesChain)).toBe('home --> contacts.list --> contacts.detail');
}));

it('generate three steps for the "contacts.detail.item" state', inject(function($breadcrumb) {
it('generate four steps for the "contacts.detail.item" state', inject(function($breadcrumb) {
goToStateAndFlush('contacts.detail.item', {contactId: 42, itemId: "a"});
var statesChain = $breadcrumb.getStatesChain();

expect(stringifyStateChain(statesChain)).toBe('home --> contacts.detail --> contacts.detail.item');
expect(stringifyStateChain(statesChain)).toBe('home --> contacts.list --> contacts.detail --> contacts.detail.item');
}));

it('generate three steps for the "contacts.detail.item.edit" state', inject(function($breadcrumb) {
it('generate five steps for the "contacts.detail.item.edit" state', inject(function($breadcrumb) {
goToStateAndFlush('contacts.detail.item.edit', {contactId: 42, itemId: "a"});
var statesChain = $breadcrumb.getStatesChain();

expect(stringifyStateChain(statesChain)).toBe('home --> contacts.detail --> contacts.detail.item --> contacts.detail.item.edit');
expect(stringifyStateChain(statesChain)).toBe('home --> contacts.list --> contacts.detail --> contacts.detail.item --> contacts.detail.item.edit');
}));

});

0 comments on commit 028e493

Please sign in to comment.