Angular-ui-router: ui-sref-active with child active - angularjs

I am using angular-ui-router and nested states in my application, and I also have a navigation bar. The nav bar is hand written, and uses ui-sref-active to highlight the current state. It is a two-level navigation bar.
Now, when I am in, say Candidate / Settings I would like both Candidate (in level 1) and Setting (in level 2) to be highlighted. However, using ui-sref-active, if I am in state Candidate.Settings then only that state is highlighted, not Candidate.
Candidate have sub-state - > bookmark, applicants
Setting have sub-state - > profile, password
I want to make active candidate and bookmark at a time while candidate should redirected to bookmark state on loading time.
I am able to make parent and child state active, but the problem is i want to load child state on clicking on candidate, and candidate and bookmark both should be active.
<ul class="ul-list">
<li ui-sref-active="current"><a ui-sref="advertiser.candidate" data-ng-click="showPage('candidatePage')">Candidates</a></li>
<li ui-sref-active="current"><a ui-sref="advertiser.settings" data-ng-click="showPage('settingsPage')">Settings</a></li>
<li class="hide block bg-green"><a class="nav-link">Help</a></li>
<li class="hide block bg-green"><a class="nav-link">Contact Us</a></li>
</ul>
<ul>
<li><a ui-sref-active="current" ui-sref=".bookmarks" data-ng-click="loadCandiatePage('bookmarks')">Bookmarks <span class="sr-only"></span></a></li>
<li><a ui-sref-active="current" ui-sref=".applicants" data-ng-click="loadCandiatePage('applicants')"> Applicants</a></li>
<li><a ui-sref-active="current" ui-sref=".hired" data-ng-click="loadCandiatePage('hired')">Hired</a></li>
</ul>
Thanks in advance!!!

You can use ng-class to set the desired class name by evaluating the $state.
To use $state if your view you need to make it available on the scope in the controller:
controller('mainCtrl', ['$scope', '$state', function($scope, $state) {
$scope.$state = $state;
}]);
Or if you want it to be available app wide without setting it in each controller you can define it on $rootScope with .run:
.run(function($rootScope, $state) {
$rootScope.$state = $state;
})
In your view you can still use ui-sref-active and also use ng-class to evaluate the current state. For example (substitute your application logic):
<li>
<a ui-sref-active="current"
ng-class="{'current': $state.current.name == 'advertiser.candidate'}"
ui-sref=".bookmarks"
data-ng-click="loadCandiatePage('bookmarks')">
Bookmarks <span class="sr-only"></span>
</a>
</li>

.state('advertiser.candidate', {
url: '/candidate',
abstract: true,
defaultChild: 'advertiser.candidate.bookmarks',
templateUrl: 'modules/users/views/advertiser/menuTabs/candidate/advertiser.candidate.html'
})
.state('advertiser.candidate.bookmarks', {
url: '/bookmarks',
templateUrl: 'modules/users/views/advertiser/menuTabs/candidate/advertiser.candidate.bookmarks.html'
})
and click on candidate page i set $state.go('advertiser.candidate.bookmarks'), then it worked for me.

Step 1: Add a Controller for your nav bar orin your existing controller where nav bar is included add the following
app.controller('navCtrl', ['$scope', '$location', function($scope, $location) {
$scope.isActive = function(destination) {
return destination === $location.path();
}
}]);
Step2: In your nav bar
<li ng-class="{active: isActive('/home')}">
<a ui-sref="app.home">Browse Journal</a>
</li>
That's it.

Related

Dynamic view names based on the parameters returned while calling the state

I am using Kendo tabstrip with angular ng-repeat and ui-router. Now the tabstrip can have different combination and different number of tabs based on the input. I am able to give different ui-sref and ui-view names to each tab while adding tabs dynamically using angular ng-repeat.
<div class="demo-section k-content">
<div kendo-tab-strip="detailsTabStrip" k-ng-delay="Tabs">
<ul>
<li ng-repeat="tab in Tabs" ng-class="getClass($first,tab)" ui-sref="{{tab.sref}}">
{{tab.name}}
</li>
</ul>
<div ng-repeat="tab in Tabs" ui-view="{{tab.view}}"></div>
</div>
</div>
So, this code gives an individual ui-view to each tab. There can 150 different types of views possible in tabs. So, I cannot hard code the view names in the router file. I need the router to accept a parameter for view name or at least in some way I can let the router know which view to populate with the html.
$stateProvider
.state('details.tab', {
url:'/tab/:tabName',
views:{
// the view name should be able to either read the state param and
// accordingly assign template or should at least be able to read the state
// and accordingly assign the template or if views can be a function.
"details.tabName": {
templateUrl: 'resources/pages/grid.html',
controller: 'gridController'
}
}
});
Is it possible to make the views names dynamic in any way. There are other parts in the application which have absolute ui-view defined and work perfectly. The changes made to make views names dynamic for this part should not affect the other parts of application. Can I use relative views to achieve this in any way? Thanks for all the help.
Also, if I am adding tabs to kendo-tab-strip dynamically using ng-repeat, can i specify a single ui-view="details" for the tabstrip. Like for all the tab states, populate details view.
<div kendo-tab-strip="tabStrip">
<ul>
<li ng-repeat="tab in Tabs" ng-class="getClass($first,tab)" ui-sref="{{tab.sref}}">
{{tab.name}}
</li>
</ul>
<div ui-view="details"></div>
</div>
If I do this, all the tab loads up fine first time but if I try to deep dive into tabs, for ex: if I try to deep dive into fifth tab, then the content for fifth tab loads up fine but its not shown. Even though the tab is shown as active(I am using ng-class as a function to decide which tab should be shown active by sending k-state-active to the appropriate tab while adding it).
And if I define different views for different tabs dynamically like below,
<div kendo-tab-strip="detailsTabStrip" k-ng-delay="Tabs">
<ul>
<li ng-repeat="tab in Tabs" ng-class="getClass($first,tab)" ui-sref="{{tab.sref}}">
{{tab.name}}
</li>
</ul>
<div ng-repeat="tab in Tabs" ui-view="{{tab.view}}"></div>
</div>
then dont know, how to tell state router which view should be populated when a particular tab is clicked. Like when third tab is clicked, the corresponding ui-view specified is ui-view="third",when fourth tab is clicked, the corresponding ui-view specified is ui-view="fourth" and so on.How to configure the views dynamically in ui-router?
Thanks a lot for all the help.
Make a common view, use that view name on your state provider.
In that view include your desiered view as
<ng-include ng-src="'resources/pages' + tabname + '.html'" />
A cleaner solution is provided here:
app.js:
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {
$urlRouterProviderRef = $urlRouterProvider;
$locationProvider.html5Mode(false);
$stateProviderRef = $stateProvider;
});
app.run(['$q', '$rootScope', '$state', '$http',
function ($q, $rootScope, $state, $http)
{
$http.get("myJson.json")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
var state = {
"url": value.url,
"parent" : value.parent,
"abstract": value.abstract,
"views": {}
};
angular.forEach(value.views, function (view)
{
state.views[view.name] = {
templateUrl : view.templateUrl,
};
});
$stateProviderRef.state(value.name, state);
});
$state.go("home");
});
}]);
Reference from AngularJS - UI-router - How to configure dynamic views

AngularJS UI Router - Nested state with individual views

I have a menu on my website, where the items have an active state. The default state is areas, then I can navigate through my website changing states.
<li ui-sref-active="active">
<a ui-sref="areas">
<span>Areas</span>
</a>
</li>
.state('areas', {
url: '/areas',
templateUrl : 'templates/areas.html',
controller : 'AreaCtrl'
})
In my areas.html template, I have a list, with items which can be clicked...:
<li ng-repeat="area in areas" ui-sref="areas.city({areaId: area.area_id, areaName: area.name})">
Now when one of these is clicked, I need to keep the active state on my menu. So the only way of doing this is via nested states...
.state('areas.city', {
url: "/:areaId/:areaName",
templateUrl: 'dist/templates/cities.html',
controller: 'CityCtrl'
})
However the problem is, my cities.html needs to be a totally new view (in place of where areas.html is), which isn't nested within areas (so I don't have a <ui-view> within areas.html).
I can, just not nest the view, so the state is .state('city', but then I lose the active state on the menu.
Any clue how I get around this?
You can activate the state in the main menu as like below
<li ng-class="{active: $state.includes('area') || $state.includes('areas.city')}">
<a ui-sref="areas">
<span>Areas</span>
</a>
</li>
Also, please apply the $state into $rootScope. So that you can access $state in any views.
myApp.run(function($rootScope, $state) {
$rootScope.$state = $state;
});

ui-router active class for parent menu when submenu selected

I am a newbie to ui-router , i would like to get a menu like below (its not a collapse thing, please see the plnkr )
menu1
a) submenu1
b) submenu2
c) submenu3
menu2
menu3
I managed to get the menus and submenus using the ui-router but not sure about the proper way to use the ui-router and used ui-sref-active="active" active the menu , the problem am facing is when I click on the submenu i would like to get the active to parent also.. ie if I click submenu1 , submenu2 or submneu3 i want to active its parent menu1.
here is the plunker : http://plnkr.co/edit/1kpmUiacrb3Aoo4E19O1?p=preview
There is an updated plunker, which does use the $state.includes method as defined here
Angular UI Router: How do I get parent view to be "active" when navigating to nested view?
the changes are: menu gets controller which puts $state into $scope:
"menu#dashboard": { templateUrl: "menu.html",
controller : function ($scope, $state){ $scope.$state = $state },
},
instead of this:
<li ui-sref-active="active"><a ui-sref=".menu1.submenu1">Menu 1</a></li>
<li ui-sref-active="active"><a ui-sref=".menu2">Menu 2</a></li>
<li ui-sref-active="active"><a ui-sref=".menu3">Menu 3</a></li>
we have to use defintion with ng-class:
<li ng-class="{active:$state.includes('dashboard.menu1')}"><a ui-sref=".menu1.submenu1">Menu 1</a></li>
<li ng-class="{active:$state.includes('dashboard.menu2')}"><a ui-sref=".menu2">Menu 2</a></li>
<li ng-class="{active:$state.includes('dashboard.menu3')}"><a ui-sref=".menu3">Menu 3</a></li>
BUT: this feature would be in a next ui-router release as we can see here:
feat(uiSrefActive): Also activate for child states.
cite
To limit activation to target state use new ui-sref-active-eq directive
Breaking Change: Since ui-sref-active now activates even when child states are active you may need to swap out your ui-sref-active with ui-sref-active-eq, thought typically we think devs want the auto inheritance.

angular-ui-router dynamic menu: TypeError: Cannot call method 'match' of undefined while rendering menu

i am new to angular and angular-ui-router and trying to create a menu.
I tried angular-ui-router and think that it fits for my needs.
now i have a problem while creating the menu with the ui-router attributes.
my html code looks like that:
<div ng-controller="MenuCtrl">
<ul>
<li ng-repeat="item in menu">
<a ui-sref="cf.{{item}}">{{item}}</a>
</li>
</ul>
</div>
the menu array contains different entries dependent on the info if the user is logged in.
now i get this error message:
TypeError: Cannot call method 'match' of undefined
<a ui-sref="cf.{{item}}" class="ng-binding">
It looks like ui-router tries to get access to the list entry before it is rendered complete with ng-repeat.
What can i do to prevent this problem?
So ui-router doesn't support interpolation in ui-sref, believe me I wish they would. Here is how I solved this.
In your controller:
app.controller("MenuCtrl", ['$scope', '$state', function($scope, $state) {
$scope.$state = $state;
}]);
In your view use an ng-href to evaluate $state object that we've now added to the $scope:
<div ng-controller="MenuCtrl">
<ul>
<li ng-repeat="item in menu">
<a ng-href={{ $state.href('cf.' + item) }}>{{item}}</a>
</li>
</ul>
</div>
You can also you this technique for wildcard params:
ng-href="{{ $state.href('cf.items.item', {'item' : item} ) }}"
Assuming you've set up your ui-router routes properly for this.

How to access route parameters outside the view

I'm writing a very simple Angular app for showing information about football tournaments. For each tournament in the database I want to show a view of either the matches or the statistics, so I'm implementing this simple URL scheme:
foo/matches: matches of tournament Foo,
foo/stats: statistics for tournament Foo.
The index.html file is structured like this:
<nav ng-controller="NavController">
<ul>
<li> Matches
<li> Stats
</ul>
</nav>
<div ng-view></div>
and the routes are configured like this:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:t/matches', {templateUrl: 'partials/matches.html', controller: 'MatchesController'})
.when('/:t/stats', {templateUrl: 'partials/stats.html', controller: 'StatsController' })
}]);
My problem are those {{ t }} links in the HTML. I want the links to change the view to the stats or the match list for the current tournament. But since the links are outside the view, so they don't have access to the route parameters (I tried injecting $routeParams and all I get is an empty object).
In summary, I haven't been able to create links for changing the view outside of it. The nav bar doesn't know what is the current tournament.
How can I (and what is the best way to) access the current state of the view (in this case, the current tournament) outside of it?
After reading a bit about scopes I learned how they nest. I solved my problem simply by wrapping both the view and the navigation bar with a new controller:
<div ng-controller="NavController">
<nav>
<ul>
<li> Matches
<li> Stats
</ul>
</nav>
<div ng-view></div>
</div>
The view will inherit NavController's scope prototypically. I initialized an object in the outer scope, and assigned the desired value to one of its properties in the inner controllers:
app.controller('NavController', function ($scope) {
$scope.currentTournament = {};
});
app.controller('MatchesController', function ($scope, $routeParams) {
// ...
$scope.currentTournament.id = $routeParams.t;
});
app.controller('StatsController', function ($scope, $routeParams) {
// ...
$scope.currentTournament.id = $routeParams.t;
});
If you are using ui-router, you could use the $stateParams service to access state and state variables from anywhere in your app.

Resources