How to setup ngclass change when route change occurs in AngularJS? - javascript

I am trying to add a class on my main .container class when the route changes.
My current simple solution involves an ng-click on every main menu url (no other urls on the page):
app.controller('MainCtrl', ['$scope', function( $scope ){
$scope.setMain = function( value ){ $scope.isMain = value };
}]);
Then in my html:
<div ng-controller="MainCtrl" class="container" ng-class="{'main': isMain}">
<ul class="main-menu">
<li><a href="#" ng-click="setMain(true)"></li>
<li><a href="#/page-1" ng-click="setMain(false)"></li>
<li><a href="#/page-2" ng-click="setMain(false)"></li>
</ul>
<div ng-view></div>
</div>
This seems to work ok as long as I need to add any more urls the problem is when a user does not click on any of the links and directly accesses a url he does not get the main class.
Is there a better way to do this?

You could use a few different options, but I think the easiest for you would be to setup the $scope.isMain value on controller initialisation by looking at $location. So inside the controller you could have something like:
var loc = $location.path();
if(loc === /* some route rule you require */){
$scope.isMain = true;
}
Have a look here: http://docs.angularjs.org/api/ngRoute/service/$route as the example down the bottom has a few different examples of what sort of data you can access with regards to the route.

Related

How can I create an array from the value of the same directive on different pages in Angular JS?

I am using Angular 1.5.7 and am trying to see if I can push the value of an attribute within a directive used on several different pages to an array that lives in the controller.
I am pretty sure that I need to used transclusion in order to do this but I am stuck. Below is a simplified version of what I have so far:
about.html
<div ng-controller="MainCtrl as ctrl"
<div cd-header mypage="About">
<div>About Page</div>
</div>
</div>
contact.html
<div ng-controller="MainCtrl as ctrl"
<div cd-header mypage="Contact">
<div>Contact Page</div>
</div>
</div>
header.html
<div>{{mypage}}
<div ng-transclude></div>
</div>
cd-header.js
var cdHeader = function() {
return {
scope: {
mypage: "#"
},
transclude: true,
templateUrl: 'header.html',
link: function(scope) {
// Not sure but I think I might need a function here
}
}
}
module.exports = cdHeader;
MainCtrl.js
var MainCtrl = function($scope) {
var nav = [];
// Not sure how items that are pushed to the nav get to this point
}
module.exports = MainCtrl;
main.js
var app = angular.module("myapp", [
'about',
'contact',
])
.controller('MainCtrl', MainCtrl)
.directive('cdHeader', cdHeader)
I am able to get the value of the mypage attribute in the directive as well as its transcluded <div> to appear in the header but only for the current page in view.
The part I am missing is how to get all of the mypage values from each page into the header regardless of the current page in view. I am somewhat new to Angular and have read a lot but have not come across anything that explains how this can be done. Maybe this is achieved with a service? If so, I'm not sure how to go about it.
To clarify with a visual. This is what I see:
But this is what I want to see:
As you rightly pointed out, there are several ways to do it.
Perhaps you can pass the array from the MainCtrl as an attribute to the directive. For instance, nav-array="nav". However, before that, you need to set the array nav as such
var $scope = this;
this.nav = [];
The second option is to consume a service. Create an angular service, pass it as a dependency in the directive, and consume it.
Lets create an array in MainCtrl as $scope.headers = ['about':'About','contact':'Contact','home':'Home'] or create a factory/service to share the headers data and in header.html use ng-repeat to display the header name according to myPage value like below
<div ng-repeat="page in headers[myPage]">{{page}}
<div ng-transclude></div>
</div>

View is getting initialized again and again

I'm building a dashboard similar to this one Admin Dashboard Theme
I'm implementing the Right Side SideBar as show in the screenshot
My App code structure is something like:
index.html:
<div ui-view=""></div>
main.html
<div ui-view=""></div>
<div ng-include='"app/dashboard/sidebar.html"'></div>
I have done nesting of view in my main.html where I'm injecting different views. Also, since my right side sidebar is fixed, I want it to be common in all the main views. So, I have just included it in my main.html.
Now, the problem is somehow my sidebar.html is getting initialized again and again no matter if I scroll my page down or perform any action inside sidebar. I have verified it by printing console logs for every controller function which are used in sidebar.html view.
This problem is related to my this post: Earlier, I wasn't able to figure out the actual issue.
Following is my controller and jade code:
angular.module('myApp')
.controller('SidebarCtrl', function($scope, $rootScope) {
$scope.message = {};
$scope.addSideBarToggleClass = function() {
console.log("addSideBarToggleClass");
return true;
}
$scope.getStatusClass = function(status) {
console.log("getStatusClass");
return 'is-online';
}
$scope.openChat = function(receiver) {
console.log("openChat");
}
// etc...
});
<aside ng-class="{ 'control-sidebar-open' : addSideBarToggleClass()}"
ng-controller="SidebarCtrl">
<ul>
<li ng-class="{active: isTabSelected('chat')}">
<a data-toggle="tab" ng-click="updateCurrenTab('chat')"></a>
</li>
<li ng-class="{active: isTabSelected('home')}">
<a data-toggle="tab" ng-click="updateCurrenTab('home')"></a>
</li>
</ul>
<div>
<div ng-class="{active: isTabSelected('home')}">
<h3>Recent Activity</h3>
</div>
<div ng-class="{active: isTabSelected('chat')}">
<div>
<h4>Chat {{noOfUsersOnline}}</h4>
<div>Friends
<a href="#" ng-repeat="user in users" ng-click="openChat(user)">
<span ng-class="getStatusClass(user.status)"></span>
{{user.name}}</a>
</div>
</div>
</div>
</div>
</aside>
I see many logs of "addSideBarToggleClass", "getStatusClass" and every time I click on openChat, I see a log of "openChat" and then again "addSideBarToggleClass" and "getStatusClass"
Can anyone please point out what can be the possible problem for this behavior?
You need to familiarize yourself with the concept of a digest loop in Angular.
In short, every time a digest loop runs, all expressions, e.g. {{name}} or ng-show="isActive && isEnabled", that are being "$watched" by Angular are evaluated (sometimes more than once). This means that if you are invoking a function inside an expression:
<div ng-show="isShown()">
$scope.isShown = function(){
console.log("expect to see this text a lot of times");
return true;
};
the function will be executed on every digest loop.
A digest loop runs any time that something in Angular calls $scope.$digest, which by default happens on things like ng-click or ng-change or $http.then, etc..

Same controller for multiple ng-app

I have a single webpage, that have different documents.php, each document with its ng-app and some controllers. (My webpage is similat to spotify).
player.php: (Where the user can play some music with an audio player)
<div class="navbar-fixed" ng-controller="menuController as menu2">
<?php include('includes/menu.php'); ?>
</div>
<div ng-controller="InteractiveController as interactCtrl">
//some code
</div>
panel_admin.php: (where the user can modify its data and some data of the webpage)
<div class="navbar-fixed" ng-controller="menuController as menu2">
<?php include('includes/menu.php'); ?>
</div>
<div ng-controller="AdminController as AdminCtrl">
//some code
</div>
player.php and panel_admin.php have its respective player.js and panel_admin.js with its .controllers.
player.js:
var decibelsInteractive = angular.module("decibels-interactive", []);
decibelsInteractive.controller('InteractiveController', function ($scope, $log) {
panel_admin.js:
var adminControl = angular.module("decibels-admin", []);
adminControl.controller("AdminController", function($scope){
Now, I have the menu bar in menu.php, where I use in player.php, and in panel_admin.php document. I import it because I don't want to repeat code in every page where I need to use the menu (menu.php have options like manage user options, logout, and many others...). I also have created a menu.js to only have the specific methods in one document and not in 25 documents.
menu.php: (menu is a menu bar where the user have some options, and it also have its username and logout option)
//this document only contains some html code
Now, I've tried to implement a simple code (in menu.js) that allows me to share one controller when I have many ng-app.
Question with the code I've taken
menu.js:
angular.module('decibels-interactive', ['shared']); //player2.php
angular.module('decibels-admin', ['shared']); //panel_admin.php
var sharedModule=angular.module('shared',[]);
sharedModule.controller('menuController',['$scope',function($scope) {
alert("menuController");
}]);
});
And this worked!!! But then, angular shows an error... (loading player.php)
Error: [ng:areq] http://errors.angularjs.org/1.2.25/ng/areq?p0=InteractiveController&p1=not%20a%20function%2C%20got%20undefined
And when I load panel_admin the same error appears again, but with the AdminController...
It seems like that angular only detects the first controller in each .php, and can't find the second one.
What am I doing wrong?
Thank you!
You can create a file for your shared code and have the controller code inside it.
// #file mySharedCode.js
var MySharedCode = MySharedCode || {};
MySharedCode.menuController = function($scope) {
// controller logic
};
Then, in your app.js file call menuController function
// #file app.js
sharedModule.controller('menuController', MySharedCode.menuController);

(angularJS) make ng-click open a link on an ng-repeat

New to Angular, so sorry if this has been covered somewhere but it's hard for me to explain exactly what I want in succinct terms.
Currently I have an ng-repeat pulling data from a JSON object and making a list.
<li ng-click="openLink()" ng-repeat="location in locations">
{{location.name}}
</li>
Each object has a key called "link" and that key has a property that is a url. I want the function openLink() to open the link associated with each object. I'm not sure how to go about this in the controller.
I know i can do
<li ng-click="openLink()" ng-repeat="location in locations">
<a ng-href="{{location.link}}">{{location.name}}</a>
</li>
but i'd like to be able to do it with ng-click instead to keep the index.html cleaner. So what do I put in here to accomplish the same thing?
$scope.openLink = function() {
};
If the url that you want to redirect to is a route in your angular app, you need to inject the $location service into the controller, then in your function, set the path property on $location.
<li ng-click="openLink(location)" ng-repeat="location in locations">
{{location.name}}
</li>
$scope.openLink = function(location){
$location.path(location.link);
}
If the location link is a complete url, you should inject the $window service into your controller, and change the openLink function to set $window.location.href.
$scope.openLink = function(location){
$window.location.href = location.link;
}

How to get AngularJS to respond to navigation

I am designing a site which has a fixed data set (17 records - each representing a 'book' for sale) and allows navigation between several pages that display details about each of the books. The problem I am having is that when the hyperlink goes to a different view, everything works fine, but if it is hyperlink is to a different book of the same view, it fails to run the controller script. How do I force this to be done.
I have made a minimal executable example:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.min.js"></script>
<script>
var fooApp = angular.module('fooApp', []);
fooApp.controller('fooCtrl', function ($scope) {
$scope.books = ["Book1", "Book2", "Book3", "Book4", "Book5"]
$scope.selectedBook = "Book1";
var curPath = window.location.toString();
var hashPos = curPath.indexOf("#/");
if (hashPos>0) {
$scope.selectedBook = curPath.substring(hashPos+2);
}
});
</script>
</head>
<body ng-app="fooApp" ng-controller="fooCtrl">
Selected Book: {{selectedBook}}
<ul>
<li ng-repeat="book in books">Visit A {{book}}
</li>
</ul>
<ul>
<li ng-repeat="book in books">Visit B {{book}}
</li>
</ul>
</body></html>
Save the above to two different files: PageA.htm and also as PageB.htm. The selected book is passed as a parameter after the hash mark. You will see the behavior as this: When on PageA you can click on a link to PageB and the book is selected appropriately. But if you click on a link to PageA the URL changes in the address bar, but the controller function does not apparently run. Similarly, on PageB, links to PageA will select a book, but links that stay on PageB do not.
I like the fact that the page is NOT re-read from the server ... it comes out of cache, but I would like to somehow trigger the the controller function again so that the new passed parameter can be read and the correct data displayed. What am I doing wrong?
Update One
Some have suggested to add target="_self" to the hyperlink tag. This works in the simplified minimal example above. Unfortunately, it does not work on fully elaborated site. I don't know why. You can check the full site and the problem is that the "Featured-Book" does not display if you are showing the details of any other book.
Ok. It seems you need to use the $location service and $locationChangeSuccess event in order to keep track url changes. These are part of the Angular core API.
fooApp.controller('fooCtrl', function ($scope, $rootScope, $location) {
$scope.books = ["Book1", "Book2", "Book3", "Book4", "Book5"]
$scope.selectedBook = "Book1";
$rootScope.$on('$locationChangeSuccess', function (event, location) {
$scope.selectedBook = $location.path().substring(1);
});
});
It seems that you are trying to implement a route system.
Have you looked into Angular's ngRoute? check out this example.
ngRoute is great because you can access route data via its $route service or simply via $routeParams.

Categories

Resources