AngularJs active navigation item Issue - angularjs

I have in view :
<ul class="nav nav-pills" ng-controller="MenuItemCtrl as vm">
<li ng-repeat="item in names" class="col-lg-2 col-md-2 col-sm-12 col-xs-12" ng-click="select($index)" ng-class="{active: $index == selected}">
<a ng-show="{{item.up===true}}" href="#{{item.link}}">{{item.name}}</a>
</li>
</ul>
and controller like this:
$scope.names = [{
name: "Main",
slug: "main",
active: true,
up:true,
inFooter:false,
link: "/"
}, {
name: "Pets",
slug: "pets",
active: false,
up:true,
link: "/pets"
}, {
name: "LDAdoption",
slug: "ldAdoption",
active: false,
inFooter:false,
up:true,
link: "/ldAdoption"
},{
name: "Impressum",
slug: "impressum",
active: false,
up:false,
inFooter:true,
link: "/impressum"
}, {
name: "Be a volunteer",
slug: "volunteer",
active: false,
inFooter:true,
up:false,
link: "/volunteer"
}, {
name: "Contact",
slug: "contact",
active: false,
up:true,
inFooter:true,
link: "/contact"
}];
$scope.selected = 0;
$scope.select= function(item) {
$scope.selected = item;
};
$scope.isActive = function(item) {
return $scope.selected === item;
};
I can't understand why active state doesn't keeping if I refresh my page. I work with ng-route and everything it's ok , just active state work wrong. Can you help me, please? Thanks!

On startup of your controller, you should fetch current route somehow and select a particular item basing on this route.
For example, you can add slug field to your routes:
$routeProvider.
when('/pets', {
templateUrl: '...',
controller: /*...*/,
slug: 'pets'
}) /* ... */;
And then read it in your controller:
function yourController($scope, $routeParams) {
/*...*/
var selectedSlug = $routeParams.slug;
if (selectedSlug) {
$scope.selected = /* find item by selected slug */
}
}

Add 'slug' to routeParams
$routeProvider.
when('/pageUrl/:slug', {
templateUrl: viewUrl,
controller: someController
});
In Controller
.controller('someController', function ($scope, $routeParams, $location) {
if ($routeParams.slug) {
$scope.selected = $routeParams.slug
}
$scope.select= function(slug) {
$location.path('/pageUrl/' + slug);
};
})
In view:
<ul class="nav nav-pills" ng-controller="MenuItemCtrl as vm">
<li ng-repeat="item in names" class="col-lg-2 col-md-2 col-sm-12 col-xs-12" ng-click="select(item.slug)" ng-show="{{item.up===true}}" ng-class="{active: item.slug == selected}">
{{item.name}}
</li>
</ul>

Related

Angular UI-Router: nested views

I have two col layout with header and footer. Header has page navigation (GetStarted, Component). Of the 2 columns, one is for sidenav and other is for main content.
When "GetStarted" nav is active, sidenav is populated with respective links (overview, examples)
When "Component" nav is active, sidenav is populated with respective links (checkbox, alert)
Upon clicking "Overview" link area is populated with its data
<ul class="nav nav-tabs">
<li role="presentation" class="active">Default</li>
<li role="presentation">Disabled</li>
</ul>
<section class="content__main__tab__content col-xs-12 col-sm-12 col-md-12 col-lg-12">
<form id="checkbox--default">
<div class="input__checkbox--default" id="checkbox--default">
<!-- <div class="form-group"> -->
<fieldset>
<legend>Default</legend>
<label for="gi-checkbox">Checkbox Label
<div class="checkbox-input-wrapper group__input-wrapper">
<input type="checkbox" class="checkbox" id="gi-checkbox">
</div>
</label>
</fieldset>
<!-- </div> -->
</div>
</form>
</section>
Main content has 2 nav tabs for checbox states (default & disable). By clicking the "default" its content must be displayed and same goes for disabled. I'm new to angular and I kinda got first level nested view working. But couldn't the whole thing working. here is the code sample
index.html
<body ng-app="mendouiApp" id="mendo__home" data-spy="scroll" data-target=".scrollspy">
<nav class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" ui-sref="home"><img src="images/gi-logo.png" alt="logo"/></a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a ui-sref="home">Get Started</a></li>
<li><a ui-sref="components">Components</a></li>
</ul>
</div><!-- /.nav-collapse -->
</div><!-- /.container -->
</nav><!-- /.navbar -->
<div class="wrapper" ui-view></div> <!--/.container-->
component.html
<div class="content__wrapper">
<div class="row">
<div class="content__secondary content__secondary--l scrollspy">
<ul id="sidenav-fixed-l" class="nav hidden-xs hidden-sm affix-top" data-spy="affix">
<li>
<h5>COMPONENTS</h5>
</li>
<li ng-repeat="item in componentsList">
<a ui-sref="{{item.link}}" ng-cloak>{{item.name}}</a>
</li>
</ul>
</div>
<div ui-view></div>
</div> <!--/.row-->
</div> <!--/.content-wraper-->
app.js
(function(){
var mendouiApp = angular.module('mendouiApp', ['ui.router', 'ui.router.stateHelper']);
mendouiApp.constant('COMPONENTS_LIST', {
name: 'sidenav',
templateUrl: '../components/components.list.html',
abstract: true,
children: [{
name: 'alerts',
url: '/alerts',
templateUrl: '../components/alerts/alerts.html'
}]
});
mendouiApp.config(function($stateHelperProvider, $urlRouterProvider, $locationProvider, $urlMatcherFactoryProvider, COMPONENTS_LIST) {
$urlMatcherFactoryProvider.strictMode(false);
$urlRouterProvider.otherwise('/home');
$locationProvider.hashPrefix('!');
$stateHelperProvider
.state('home', {
url: '/home',
templateUrl: '../gettingstarted.html',
controller: 'getStartedController'
})
.state('layouts', {
url: '/layouts',
templateUrl: '../layouts.html'
})
.state('screenpatterns', {
url: '/screenpatterns',
templateUrl: '../screenpatterns.html'
})
.state('yogi', {
url: '/yogi',
templateUrl: '../yogi.html'
})
.state('components', {
url: '/components',
templateUrl: '../components.html',
controller: 'componentsController'
})
.state(COMPONENTS_LIST, {
keepOriginalNames: true
})
.state('components.button', {
url: '/button',
templateUrl: '../components/button/button.html'
}) .state('components.checkbox', {
url: '/checkbox',
templateUrl: '../components/checkbox/checkbox.html'
})
.state('components.forms', {
url: '/forms',
deepStateRedirect: true,
sticky: true,
views: {
'': { templateUrl: '..forms.html' },
'inline#components.forms': {
templateUrl: '../components/forms/form-inline/forminline.html'
},
'default#components.forms': {
templateUrl: '../components/forms/form-default/formdefault.html'
},
'multicolumn#components.forms': {
templateUrl: '../components/forms/form-multicolumn/formmulticolumn.html'
}
}
});
// use the HTML5 History API
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
});
mendouiApp.controller('componentsController', ['$scope', '$state', 'sideNavService', function($scope, $state, sideNavService, COMPONENTS_LIST){
$scope.componentsList = sideNavService.components;
$scope.componentsnav = COMPONENTS_LIST.children;
$scope.go = function(tab) {
$state.go(tab.name);
}
}]);
mendouiApp.controller('getStartedController', ['$scope', '$state', 'sideNavService', 'fixedSideNavService', function($scope, $state, sideNavService, fixedSideNavService ){
$scope.getstartedList = sideNavService.getstarted;
}]);
/*** This is for the external url reference ***/
mendouiApp.run(function($rootScope, $state, $stateParams, $window, fixedSideNavService, copyToClipBoardService) {
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams, $state, $stateParams) {
if (toState.external) {
event.preventDefault();
$window.open(toState.url, '_self');
}
});
$rootScope.$on('$viewContentLoaded', function(event){
fixedSideNavService.fixedsidenav();
copyToClipBoardService.copytoclipboard();
});
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$state.transitionTo('home');
});
})();
service.js
angular.module('mendouiApp').service('sideNavService', function() {
return {
"getstarted" : [
{
"name" : "Overview",
"link" : "home.overview"
}
{
"name" : "Summary",
"link" : "home.overview"
}
],
"components" : [
{
"name" : "Alerts",
"link" :"components.alert"
},
{
"name" : "Button",
"link" :"components.button"
},
{
"name" : "Button Groups",
"link" :"components.buttongroup"
},
{
"name" : "Button Icons",
"link" :"components.buttonicons"
},
{
"name" : "Checkbox",
"link" :"components.checkbox"
},
{
"name" : "Datepicker",
"link" :"components.datepicker"
},
{
"name" : "Forms",
"link" : "components.forms"
}
]
};
});
Your question was a bit messy, but after a while playing with I could understand I made this fiddle: http://jsfiddle.net/canastro/c4kt2myc/2/ I hope it works as you were expecting.
The main thing to focus on here is:
.state("root.components.button", {
url: '/components/button',
views: {
'main#': {
template: `
<div>
<a ui-sref="root.components.button.default">default</a>
<a ui-sref="root.components.button.disabled">disabled</a>
<div ui-view="buttonsContent"></div>
</div>
`
}
}
})
.state("root.components.button.default", {
url: '/components/button/default',
views: {
'buttonsContent#root.components.button': {
template: 'root.components.button.default'
}
}
})
.state("root.components.button.disabled", {
url: '/components/button/disabled',
views: {
'buttonsContent#root.components.button': {
template: 'root.components.button.disabled'
}
}
})
In the first level you have a abstract route so you can always have your basic layout present.
Then in the Started / Components routes, you load content into the main and side ui-views.
In all of the Started and Component child routes you just override the main views.
And finally, in the last level you need to say you want to fill the content of a ui-view created in the previous state, by doing something like VIEWNAME#STATENAME.

angularjs accordion list anchor alternate elements

I have an accordion list populated as follows:
<ion-list>
<div ng-repeat="day in days"><br>
<div class="item item-icon-left" ng-click="toggleGroup(day)" ng-class="{active: isGroupShown(day)}">
<i class="icon icon-accessory" ng-class="isGroupShown(day) ? 'ion-minus' : 'ion-plus'"></i>
{{day.name}}
</div>
<a class="item item-icon-left item-accordion" ng-show="isGroupShown(day)" ng-repeat="exercise in day.exercises" type="item-text-wrap"
href="#/tab/plan/{{exercise.id}}">
{{exercise.name}}
<!-- Trial LED -->
<span style="float:right;"><i ng-show="{{exercise.watchedToday}}==true" class="ion-checkmark-round"></i></span>
</a>
</div>
</ion-list>
This populates list as follows:
Route which handles this is:
.state('tab.exercise', {
url: '/plan/:exerciseId',
resolve: {
authenticated: ['djangoAuth', function(djangoAuth){
return djangoAuth.authenticationStatus();
}],
},
views: {
'tab-plan': {
templateUrl: '/templates/video.html',
controller: 'videoCtrl'
}
},
cache: false
})
However I have few other routes like:
.state('tab.wordschatz', {
url: '/plan/12', <= notice here (this can be 8/9/10/11/12)
resolve: {
authenticated: ['djangoAuth', function(djangoAuth){
return djangoAuth.authenticationStatus();
}],
},
views: {
'tab-plan': {
templateUrl: '/templates/wortschatz.html',
controller: 'wortschatzCtrl',
data: {
css: '/css/wortschatz.css'
}
}
},
cache: false
})
I basically want to navigate to different pages on alternate accordion elements clicks. How can I have separate anchor links for alternate elements?

How to use HTML inside conditional bind in markup

I have markup like this
<p>{{ !job.AdditionalNotes ? "No additional notes." : job.AdditionalNotes }}</p>
Would like to emphasis No Additional notes using something like.
<p>{{ !job.AdditionalNotes ? <em>"No additional notes."</em> : job.AdditionalNotes }}</p>
Is there a way to do this without using ng-if and ng-show to do this retaining the ternary operator?
1st Option
I get this working in the following way (without ng-show or ng-if). I'm using ng-bind-html and $sce service to render the HTML. Since your "no additional notes" message is generic and common, we can easily define in the controller and get it from a method after sanitization.
var app = angular.module("sa", []);
app.controller("FooController", function($scope, $sce) {
$scope.jobs = [{
name: "Sr. Software Developer"
}, {
name: "Software Associates",
AdditionalNotes: "Remote location"
}, {
name: "Front-end developer"
}];
$scope.trust = function(text) {
return $sce.trustAsHtml(text);
};
$scope.noNotesMessage = function() {
return $scope.trust("<em>No additional notes.</em>")
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" ng-controller="FooController">
<ol>
<li ng-repeat="job in jobs">
<strong>{{job.name}}</strong>
<p ng-bind-html="!job.AdditionalNotes ? noNotesMessage() : trust(job.AdditionalNotes)"></p>
</li>
</ol>
</div>
2nd Option
Alternatively, you can write a directive:
var app = angular.module("sa", []);
app.controller("FooController", function($scope, $sce) {
$scope.jobs = [{
name: "Sr. Software Developer"
}, {
name: "Software Associates",
AdditionalNotes: "Remote location"
}, {
name: "Front-end developer"
}];
});
app.directive('notes', function() {
return {
restrict: 'E',
scope: {
additional: '='
},
link: function($scope, element, attr) {
var html = "<p>" + ($scope.additional || "<em>No additional notes.</em>") + "</p>";
element.html(html);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" ng-controller="FooController">
<ol>
<li ng-repeat="job in jobs">
<strong>{{job.name}}</strong>
<notes additional="job.AdditionalNotes"></notes>
</li>
</ol>
</div>

Wiring up a nav menu with Angular routes

I'm working with an asp.net mvc4 and Angular JS (started from John Papa's HotTowel.angular.Breeze SPA). I have added a dropdown menu to the sidebar.html, and have also added the appropriate routes to config.route.js.
I find the tricky part is to properly wire-up the dropdown menus to the Angular routes.
This setup is mostly working, except that when I click on the "MR Reports" dropdown menu below, it's right-away firing a route (i.e. when I hover over MR Reports, it shows the link "localhost:49479/index.html#". In other words, the "MR Reports" should be a dropdown menu and NOT trigger a route change until I choose a dropdown menu item.
I'm not sure if the change should be in config.route.js or in the sidebar.html code.
UPDATE:
I'm assuming I can use the Angular ngHref directive, something like ng-href="#{{r.url}}", to dynamically exclude the top-level href tag if there's a sub menu.
Perhaps I can use ng-if="r.config.sub" as a guide as well.
Here is the contents of config.route.js (please note the "sub:" section for the sub-menus):
(function () {
'use strict';
var app = angular.module('app');
// Collect the routes
app.constant('routes', getRoutes());
// Configure the routes and route resolvers
app.config(['$routeProvider', 'routes', routeConfigurator]);
function routeConfigurator($routeProvider, routes) {
routes.forEach(function (r) {
$routeProvider.when(r.url, r.config);
});
$routeProvider.otherwise({ redirectTo: '/' });
}
// Define the routes
function getRoutes() {
return [
{
url: '/',
config: {
templateUrl: 'app/dashboard/dashboard.html',
title: 'dashboard',
settings: {
nav: 1,
content: '<i class="fa fa-dashboard"></i> Dashboard'
}
}
}, {
url: '/admin',
config: {
title: 'admin',
templateUrl: 'app/admin/admin.html',
settings: {
nav: 2,
content: '<i class="fa fa-lock"></i> Admin'
}
}
},
{
url: '/testgrid',
config: {
title: 'grids',
templateUrl: 'app/testgrid/testgrid.html',
settings: {
nav: 3,
content: '<i class="fa fa-globe"></i> TestGrid'
}
}
},
{
config: {
title: 'reports',
templateUrl: 'app/testgrid/testgrid.html',
settings: {
nav: 4,
content: '<i class="fa fa-plus-square "></i> MR Reports <b class="caret"></b>'
},
sub: [
{
url: '/testgrid',
title: 'reports1',
templateUrl: 'app/testgrid/testgrid.html',
settings: {
content: 'TestGrid'
}
},
{
url: '/admin',
title: 'Admin2',
templateUrl: 'app/admin/admin.html',
settings: {
content: 'Admin2'
}
}
]
},
}
];
}
})();
and my sidebar.html is defined as :
<div data-cc-sidebar data-ng-controller="sidebar as vm">
<div class="sidebar-filler"></div>
<div class="sidebar-dropdown">Menu</div>
<div class="sidebar-inner">
<div class="sidebar-widget">
</div>
<ul class="navi">
<li class="nlightblue fade-selection-animation"
ng-class="{dropdown: r.config.sub}"
data-ng-repeat="r in vm.navRoutes">
<a href="#{{r.url}}" data-ng-bind-html="r.config.settings.content" ng-class="{'dropdown-toggle': r.config.sub}" data-ng-class="vm.isCurrent(r)" data-toggle="dropdown" ></a>
<ul ng-if="r.config.sub" class="dropdown-menu">
<li ng-repeat="submenu in r.config.sub">
</li>
</ul>
</li>
</ul>
</div>
I'm not crazy about my hack, but it ended up working. I used one ng-if to render the href link for non-dropdown menu items, and another ng-if to EXCLUDE the href tag for the dropdowns.
And in the getRoutes() function, I EXCLUDE the url: property at the top-level of the dropdown.
<ul class="navi">
<li class="nlightblue fade-selection-animation"
ng-class="{dropdown: r.config.sub}"
data-ng-repeat="r in vm.navRoutes">
<a ng-if="r.url" href="#{{r.url}}" data-ng-bind-html="r.config.settings.content"
ng-class="{'dropdown-toggle': r.config.sub}"
data-ng-class="vm.isCurrent(r)"
data-toggle="dropdown" ></a>
<a ng-if="r.config.sub" data-ng-bind-html="r.config.settings.content"
ng-class="{'dropdown-toggle': r.config.sub}"
data-ng-class="vm.isCurrent(r)"
data-toggle="dropdown" ></a>
<ul ng-if="r.config.sub" class="dropdown-menu">
<li ng-repeat="submenu in r.config.sub">
</li>
</ul>
</li>
config.route.js function:
function getRoutes() {
return [
{
url: '/',
config: {
templateUrl: 'app/dashboard/dashboard.html',
title: 'dashboard',
settings: {
nav: 1,
content: '<i class="fa fa-dashboard"></i> Dashboard'
}
}
}, {
url: '/admin',
config: {
title: 'admin',
templateUrl: 'app/admin/admin.html',
settings: {
nav: 2,
content: '<i class="fa fa-lock"></i> Admin'
}
}
},
{
url: '/testgrid',
config: {
title: 'grids',
templateUrl: 'app/testgrid/testgrid.html',
settings: {
nav: 3,
content: '<i class="fa fa-globe"></i> TestGrid'
}
}
},
{ // Exclude url: at this top level
config: {
title: 'reports',
settings: {
nav: 4,
content: '<i class="fa fa-plus-square "></i> MR Reports <b class="caret"></b>'
},
sub: [
{
url: '/testgrid',
title: 'reports1',
templateUrl: 'app/testgrid/testgrid.html',
settings: {
content: 'TestGrid'
}
},
{
url: '/admin',
title: 'Admin2',
templateUrl: 'app/admin/admin.html',
settings: {
content: 'Admin2'
}
}
]
},
}
];
}

AngularJS: Custom directive using dynamic attribute value doesn't work inside "ng-repeat"

Could you explain please why the following directive doesn't work?
attrs.ngMydirective seems to be undefined inside the linking function.
Live example here
HTML:
<body ng-controller="MyCtrl">
<ul>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective="{{ person.age }}"></span>
</li>
</ul>
</body>
JS:
var app = angular.module('myApp', []);
app.directive('ngMydirective', function() {
return {
replace: true,
link: function(scope, element, attrs) {
if (parseInt(attrs.ngMydirective, 10) < 18) {
element.html('child');
}
}
};
});
app.controller('MyCtrl', function($scope) {
$scope.people = [
{name: 'John', age: 33},
{name: 'Michelle', age: 5}
];
});
You should use attrs.$observe to have actual value.
Another approach is to pass this value to directive's scope and $watch it.
Both approaches are shown here (live example):
var app = angular.module('myApp', []);
app.directive('ngMydirective', function() {
return {
replace: true,
link: function(scope, element, attrs) {
attrs.$observe('ngMydirective', function(value) {
if (parseInt(value, 10) < 18) {
element.html('child');
}
});
}
};
});
app.directive('ngMydirective2', function() {
return {
replace: true,
scope: { ngMydirective2: '#' },
link: function(scope, element, attrs) {
scope.$watch('ngMydirective2', function(value) {
console.log(value);
if (parseInt(value, 10) < 18) {
element.html('child');
}
});
}
};
});
app.controller('MyCtrl', function($scope) {
$scope.people = [
{name: 'John', age: 33},
{name: 'Michelle', age: 5}
];
});
<body ng-controller="MyCtrl">
<ul>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective="{{ person.age }}"></span>
</li>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective2="{{ person.age }}"></span>
</li>
</ul>
</body>

Resources