View is getting initialized again and again - javascript

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..

Related

Unable to access user input in a function called from ng-repeat

I am new to Angular and may be missing something obvious. I did read the ng-model scope documentation. And followed the recommendation to use an object instead of primitives.
I also tried the $parent suggested by a few others here. That didn't help either.
I have read many Q&As here involving ng-repeat. But they are talking about an input inside an ng-repeat. In my case there is only one text box. I am trying to search inside a tree using ng-repeat.
<script type="text/ng-template" id="tree_item_renderer.html">
{{data.name}}
<ul>
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-show="visible(data)"></li>
</ul>
</script>
This is the function being called:
$scope.visible = function(item) {
console.log("SearchText inside visible: " + $scope.input.searchText);
return !($scope.input.searchText && $scope.input.searchText.length > 0
&& item.title.indexOf($scope.input.searchText) == -1);
};
Take a look at my jsFiddle
I saw your code on the jsFiddle. There is an issue that you called ng-controller="TreeController" twice and initialized ng-app = Application. I have commented out these two lines of code, and you can see the change on the console.
The reason why you are getting this problem is you make Angular to re-initialize TreeController which it clears the value of input.searchText.
Check out the working jsFiddle !
<div ng-app="myApp">
<div ng-controller="TreeController">
Search UIC:
<input type="text" ng-change="search()" ng-model="input.searchText"
placeholder="Enter your search terms" />
<!-- <ul ng-app="Application" ng-controller="TreeController"> -->
<ul>
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'" ng-show="visible(data)">
</li>
</ul>
</div>
</div>

AngularJS: Binding not working correctly

I have a login page that displays inside my index.html page. The index.html page has control over a navigation bar that I don't want to show up until the user has successfully logged in. After they login however, the navigation bar ng-show attribute doesn't get updated as I have it in code. Here is the code to illustrate:
<nav class="navbar navbar-inverse" ng-controller="loginController" ng-show={{valid}}>
<div class="collapse navbar-collapse navbar-collapse-links">
<ul class="nav navbar-nav">
<li><a ui-sref="addUser" class="addUser" >Add User</a></li>
<li><a ui-sref="listUser" class="mdUser">Modify User</a></li>
<li><a ui-sref="login" class="mdUser">Logout</a></li>
</ul>
</div>
</nav>
Inside my controller looks like this:
$scope.valid = false;
function login() {
AuthenticationService.Login(vm.user.username, vm.user.password, function (response) {
if (response.success) {
AuthenticationService.SetCredentials(response.token.UserName, response.token.TokenId);
$scope.valid = true;
$location.path('/');
} else {
//FlashService.Error(response.message);
vm.error = response.message;
vm.dataLoading = false;
}
....
)};
}
These is a crude copying and pasting of the code that only contains what I believe is necessary for the current issue.
I can manually change the declaration to true or false and have the navigation bar act correctly. Otherwise, the navigation bar doesn't show up after a valid login attempt, and when I check the html in the console after the login, the ng-show method still shows as false.
QUESTION: Why isn't the valid binding correctly to change after it is reassigned in my success function?
If your AuthenticationService doesn't pump the digest loop, Angular will not update its bindings because it won't know that something has changed.
Try wrapping the body of your .Login callback in a $scope.$apply().
Also, you're navigating away from this view immediately after authenticating the user so the scope where you set $scope.valid = true; is gone.
You can try setting a flag on the root scope like: $rootScope.isLoggedIn = true; and bind against that since the rootscope is kept around for the life of the application.
You're best option is to store the authentication state in your service and binding against that instead. Since services are singletons, that state will be preserved between views as well.
Remove the {{ }} from ng-show. That is one issue with your code.
ng-show="valid"
And check this out for some more info:
http://jaketrent.com/post/when-to-use-curlies-in-angular-directives/
try to change,
<nav class="navbar navbar-inverse" ng-controller="loginController" ng-show={{valid}}>
to
<nav class="navbar navbar-inverse" ng-controller="loginController" ng-show="valid">

Trigger function on a certain element - ngrepeat - angularjs

Good morning,
I'm trying to change the limitTo filter on a certain list, my issue is:
when I click to the trigger who change the filter limit the filter changes on all ng-repeated categories.
my function inside the main controller
$scope.showMore = function(limit) {
if($scope.limitItems === $scope.itemsPerList) {
$scope.limitItems = limit;
$scope.switchFilterText = 'less';
} else {
$scope.switchFilterText = 'more';
$scope.limitItems = $scope.itemsPerList;
}
}
my scenario (I rewrote it in a simplified version)
<li ng-repeat="item in category.items | limitTo: limitItems ">
{{item.title}}
</li>
<li ng-if="limitItems < (category.items.length)">
<a ng-click="showMore(category.items.length)" >Show {{ switchFilterText }}</a>
</li>
Could you explain me what's wrong with me?
I searched how to select a single element to apply the function but I didn't find anything useful
Update:
I found the way to solve my issue in this way:
No functions inside the controller are involved to make this functionality works properly:
<li ng-repeat="category in maincategories" ng-init="limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: limitItems "> {{item.title}}
</li>
</ul>
<a ng-click="limitItems = category.items.length" href>
<b ng-if="category.items.length > maxItemsPerList && limitItems != category.items.length "> Show more </b>
</a>
I'm not really convinced about Angular (I used it in my past and I was impressed by the performance but now I can see logics senseless):
What I learned:
ng-if and ng-click cannot be used in the same content because ng-if creates new scopes so if you put ng-if on top of the "show more" link it will break the code
ng-init cannot be used in the same element of the ng-repeat otherwise the var initialised will not be available inside the ng-repeat block
I think there is another way to do that, maybe more clean but in this specific case I can't do a lot.
ng-if and ng-click cannot be used in the same content because ng-if
creates new scopes so if you put ng-if on top of the "show more" link
it will break the code
Yes, ng-if creates a new scope, but it is possible to mix ng-if and ng-click (and most other directives). To do that, you'll be safer if you always write to atributes of another object instead of a simple variable. It is plain JavaScript prototypal inheritance in play.
<li ... ng-init="category.limitItems = maxItemsPerList">
ng-init cannot be used in the same element of the ng-repeat otherwise
the var initialised will not be available inside the ng-repeat block
True, in the sense that variables are created in the local scope. But again, refer to an object.
I think there is another way to do that, maybe more clean but in this
specific case I can't do a lot.
You don't need to do a lot, it is quite simple to do it right actually.
Some advices:
Use ng-init with care. I know it will tempt us but always try to put logic inside controllers and services;
Avoid assignments inside templates;
Learn how to use controllerAs syntax. It gives you an object to write your models to (the controller), so solves most problems related to scope inheritance;
Do not inject $scope, put your view models inside controllers.
Full code goes like this:
<li ng-repeat="category in maincategories" ng-init="category.limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: category.limitItems "> {{item.title}}
</li>
</ul>
<a ng-if="category.items.length > maxItemsPerList && category.limitItems != category.items.length" ng-click="category.limitItems = category.items.length" href>
<b> Show more </b>
</a>

Implementing a page load performance directive in AngularJS

I am very new with AngularJS so I need quite some pointing in the right direction.
The task is to create some kind of widget that displays how much time it takes from any user action until the requested page finishes rendering.
We are going to be using AngularJS at the presentation layer and the back-end will be Microsoft's Web API.
So I figured I could use the browser's Navigation Timing API and wrap it on an AngularJS directive so I tried this:
angular.module('performanceDirective', [])
.directive('pagePerformance', function(){
return {
restrict: 'AE',
replace: 'true',
template: '<div><label id="loadTimeEllapsed">Total Load Time:{{totalLoadTime}}</label></div>',
scope: {},
link: function (scope, elem, attrs) {
scope.$watch('window.performance.timing', function (newValue, oldValue) {
var timing = window.performance.timing;
var userTime = timing.loadEventEnd - timing.navigationStart;
scope.totalLoadTime = userTime;
});
}
};
});
But it seems that there is something missing because even though I am doing actions that call the back-end the number that gets displayed after the home page loads is never updated.
Is this something that actually would work, provided we fix whatever is failing, or is this a dead end and we need to find another option?
UPDATE
The use of the directive has nothing to it, basically it is just the element thrown on a page:
<body ng-app="myApp">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li>Some Action</li>
</ul>
</div>
</div>
<div class="row">
<div class="span4"><data-page-performance /></div> <!-- The Directive -->
<div class="span10" ng-view></div>
</div>
</body>
Apparently this directive only works if I refresh the page after I have already navigated to it but if I click on an element that will trigger an action on the AngularJS controller the performance number is completely unaffected.

AngularJS $scope data not rendering in view

I have been struggling with this problem for the past few days, and now i believe its time to reach out to the community for some help.
I am creating a website using the MEAN stack, this is my first time using Angular JS so i am a noobie.
The problem I am having is, I am not able to render ANY $scope data in my view. this has been driving me crazy because the AngularJS chrome debugger shows that the $scope data is there!!! But it wont render in my template.
App Structure
/public
/js
/controllers
-user.js
-app.js
/views
-index.html
Contents of my html - i will not copy entire file, only angular parts.
<!doctype html>
<html ng-app="PCT">
<div ng-controller="userController" class="column">
<a class="dropdown-toggle" data-toggle="dropdown" href="#userinfo" >
<img ng-src="{{user.img}}" style="width:40px; height:50px;" />
</a>
<ul class="dropdown-menu" role="menu">
<li><strong>{{user.name}}</strong></li>
<li><a ng-click="logOut()">Sign Out</a></li>
<li>Admin Site</li>
</ul>
</div>
<script src ="js/app.js"></script>
<script src="js/controllers/user.js"></script>
Contents of my app.js
angular.module('PCT.userController',[]);
//Importing all modules into main module
angular.module('PCT',['PCT.userController']);
Contents of my user.js (Controller)
angular.module('PCT').controller('userController', ['$scope', function($scope){
$scope.user = {
img : 'http://path/to/usr/img/user.jpg',
name : 'mcutalo'
};
$scope.logOut = function(){
console.log('logging out');
}
}]);
I am able to click on the Logout button in my menu, and this will trigger the console log, so it is making it to the controller. But it wont display my $scope data at all.
I am not sure what your question was, but if "PCT" is your module (it should match the name ng-app and the app.module("PCT") and no need to inject controller if u are defining controller using
angular.module('PCT').controller('userController', ['$scope', function($scope){
}]);
Here is the plnkr of the working one.
Updated with the Logout and it is called console.log...
http://plnkr.co/edit/uSLVqEayCh2XeeGI7LZe?p=preview
Let me know if any further help is needed.

Categories

Resources