angular result and digest in code - angularjs

I have a simple controller, i want to get a random value on loading, then use it to paint an arc somewhere, but it gives many values and it paints several arcs. I think it's cause of angular refreshing, but can I just take final value, not all?
angular.module("app", [])
.controller("Controller", ['$scope', function($scope)
{
$scope.sample = {};
$scope.sample.first = Math.floor(4+Math.random()*3);
$scope.sample.arc = function(unitstart, number)
{ paint arc using first
};
$scope.samle.arc(arguments);
}]);
If not talking about arcs, just place some element, like shown below, it won't get final value of random expression evaluation, but some value of between of evaluation of it.
<span ng-controller = "Controller">{{sample.first}}</span>

Try this instead:
$scope.sample[0].first = Math.floor(4+Math.random()*3);
And in the markup <span ng-controller = "Controller">{{sample[0].first}}</span>
And see if you'll get any value
I think you need to initialize the array as well in order to be able to access the properties.

Related

Change language - screen goes white

How to properly use a checkbox to change $rootScope value in AngularJS
I am trying to create a multilingual section to an app, that allows the users to front to choose their language of preference when the app loads.
In doing so, I set the $rootScope.language to their preferred language.
Since that occurs only once at the load of the app, I wanted to add a preferences section where they can alter their choice, as needed.
In my Ionic V1 framework, I had created this in my template for the users to choose their language:
<ion-list>
<ion-radio ng-model="lang" ng-value="'en_US'" ng-click="updateLanguage('en_US')">English</ion-radio>
<ion-radio ng-model="lang" ng-value="'es_MX'" ng-click="updateLanguage('es_MX')">Espanol</ion-radio>
<ion-radio ng-model="lang" ng-value="'fr_CA'" ng-click="updateLanguage('fr_CA')">Francais</ion-radio>
</ion-list>
Here is part of my app.js that sets the $rootScope value for the language:
.run(function($rootScope) {
$rootScope.language = 'en_US';
})
Along with that, I was using this in my controllers.js:
.controller('PreferencesCtrl', function($scope, $rootScope, $state, $ionicPlatform, $ionicLoading) {
alert('PreferencesCtrl');
$scope.lang = $rootScope.language;
//Update Language
$scope.updateLanguage = function(lang) {
$scope.lang = lang;
$rootScope.language = lang;
};
});
The issue I am running into is that I can successfully change the language once, but if I attempt to change it again, the whole page goes white, and nothing is being rendered. It also appears that by setting the $rootScope, it also changes the view that is being rendered, as the back button goes away on the current screen.
My guess is that I am not setting the $rootScope.language in the updateLanguage function as the information is not being logged to the console on the second attempt to change the language.
If I change the updateLangauge to this:
//Update Language
$scope.updateLanguage = function(lang) {
$scope.language = lang;
};
Then I don't experience any of the white screen issues, however, the language is only changed for that particular view - not across the entire app.
Any thoughts on where I may be approaching this incorrectly?
UPDATE: It appears that after the $rootScope is being set again in the updateLangauge function that my PreferencesCtrl is then not being executed. I added an alert to the controller to see if it was firing after the $rootScope was set for the second time (when the screen goes white) and the alert never fires. So, it's like the controller is gone after you update the $rootScope.
What i think is going on here, is you're not updating your ng-model.
Whenever you click a language, you update your ng-model, and also pass the language to a method to update the $rootScope.language.
Change your method to update both:
$scope.updateLanguage = function(lang) {
$rootScope.language = lang;
$scope.lang = lang;
};
This should update both the UI ng-model and the rootscope value, making it available everywhere.
The only reason i can think of why your page goes white is because you have some kind of error in the debug console. check that. And you also have a typo in your App.js: it says $rootScope.langauge (should be language).
Furthermore, it might also be interesting to just keep a list of languages in your controller, rather than hardcoding them in the html.
So you could iterate over that, and use those values to update the rootscope.

$scope and counting children of an element in AngularJS

I'm pretty new to AngularJS, and I have a jQuery background so that could influence my way of thinking the problem.
In order to do DOM manipulation through transclude directive (i.e. adding a specific class) I need to know how many children (or maybe siblings) has a generic element.
What I mean is I would like to set a class on all children, based on an algorithm that counts the number of children themselves.
This is what I tried so far
var main = angular.module("Main",[]);
function utilities(){
this.consoleScope = function($scope){
return $scope.children().length;
};
}
main.service("utilities",[utilities]);
main.controller("Prova",["$scope","utilities",function($scope,utilities){
var self = this;
self.consoleScope = function(){
return utilities.consoleScope($scope);
};
}]);
But even if it runs without errors, it doesn't retrieve the information I wanted. I can comprehend this is not the right way to do this, but I can't see any other way. What could I try?
So you've mixed up your application logic with your DOM logic. Ideally when talking about children you'd be assigning or creating these based upon a data set or collection.
e.g.
//In your controller
$scope.data = someDataSet;
from there you would implement your algorithm based upon your data set.
//still in your controller
$scope.algorithm = function(data){
... implement your logic ...
// e.g.
return data.length > 5;
}
Now in your UI mark up you would use ng-class and an expression to assign the class on any element that needs the class. Your controller shouldn't know about your classes.
<div ng-class="(algorithm(data)) ? 'trueClass' : 'falseClass'" ></div>
This is a really simple implementation but you can extend it pretty easily.
https://docs.angularjs.org/api/ng/directive/ngClass

Using ng-class with a function call - called multiple times

I'm using Ionic and want to dynamically change the background colour of each item in an <ion-list> based on the data. I thought I'd do this by way of a function call to return the correct class
<ion-list>
<ion-item ng-repeat="singleCase in allCases" ng-class="getBackgroundColour(singleCase)" class="item-avatar">
<h2>{{singleCase.date}}</h2>
<p>{{singleCase.caseType}}</p>
</ion-item>
</ion-list>
This is the controller at present
.controller('AllCasesCtrl', ['$scope', '$log', 'dummyData', function($scope, $log, dummyData) {
$scope.allCases = dummyData.cases;
$scope.getBackgroundColour = function(singleCase){
$log.log("getBackgroundColour called...singleCase type: " + singleCase.speciality);
var colourMap = {
speciality1: "speciality1Class",
speciality2: "speciality2Class",
speciality3: "speciality3Class"
};
return colourMap[singleCase.speciality];
};
}])
On checking the console, the getBackgroundColour() function is being called 7 times for each <ion-item> in the list. Why is this, and should I avoid using a function call in ng-class?
AngularJS works with dirty checking: it needs to call the function each cycle to be sure that it doesn't return a new value and that the DOM doesn't need to be updated.
It's part of the typical process of the framework, and calling a function as simple as yours shouldn't have any negative performance impact. Readability and testability of your code is far more important here, so keep the logic in your controller.
One simple things to do, however, is simply to move the declaration of colourMap, which is a constant, outside of your function:
var colourMap = {
speciality1: "speciality1Class",
speciality2: "speciality2Class",
speciality3: "speciality3Class",
};
$scope.getBackgroundColour = function(singleCase) {
return colourMap[singleCase.speciality];
};
Your way is fine as long as your list is not some huge size. That being said if you are using angular 1.3 and you want to lower the number of calls you can change your ng-class to ng-class="::getBackgroundColour(singleCase)". This applies one time binding so once the value is stable it will not check again. This would most likely mean two calls per item.

Why is this javascript defined # AngularJS 1.2.26 Controller being called for every single click on the page? Strange behavior

Hi I've been developing my first AngularJS application and studying Angular for about 4 months using my free time, so I am far for being an expert.
I just notice in one of my Controllers that there's a function defined to calculate the effort between two dates that's being called for every single click that happens in that page. I mean even if I have a simple button that just shows or hides parts of the view or even using angular-ui calendar component button to show the calendar it triggers that function. I have no clue why is this happening.
Here's some fragment of code:
My Controller definition:
'use strict';
(function () {
var byallApp = angular.module('byallApp');
byallApp.controller('ActivitieController', ['$scope', '$log', 'httpGetService', '$rootScope', 'httpPostService', '$moment',
function ($scope, $log, httpGetService, $rootScope, httpPostService, $moment) {
$scope.activities = [];//array that holds the objects to be displayed in table.
....//a lot of normal code here. No code at all that updates the $scope.activities array is ever called outside some other function.
//function that calculates the effort, uses momentjs
this.calculateEffortFromValues = function (finalDate, initialDate) {
$log.info('Executing calculateEffortFromValues');
var initial = $moment(new Date(initialDate));
var final = $moment(new Date(finalDate));
var duration = $moment.utc(final.diff(initial)).format("HH:mm");
$log.info('duration: ' + duration);
return (duration);
}
}]);
})();
Than in my view I use the controller and angular directives to render the table using the $scope.activities array:
<div ng-controller="ActivitiesController as activitiesCtrl">
....
<tbody>
<tr ng-repeat="activityList in activities">
<td>{{activityList.initialDate | date : 'dd/MM/yyyy'}}</td>
<td>{{activityList.initialDate| date : 'hh:mm a'}}</td>
<td>{{activityList.endDate | date : 'hh:mm a'}}</td>
**<td>{{activitiesCtrl.calculateEffortFromValues(activityList.endDate, activityList.initialDate)}}</td>**
<td>{{activityList.codContract}}</td>
<td>{{activityList.description}}</td>
<td>
<button class="btn btn-danger btn-mini" ng-click="deleteRow(row)" ng-hide="isTemp($index)"><img
width="25px" height="25px" title="Delete Activity!" src="img/trash.ico"/></button>
</td>
</tr>
</tbody>
I than call that function while building the table to calculate the effort based on 2 other fields of the table as showed above.
All works perfeclty as expected. But than reviewing the code and with the open debugger console in Chrome I noticed that for every single click I have no this page, this function is called again. I start thinking that somehow my $scope.activities array would probably being updated but I double checked and this doesn's seem to be the case as it's also only updated inside functions where I also log to console and that functions are never called.
Any clues about what could be causing this strange behavior?
When you bind values in your HTML code, with {{ }}, you basically ask Angular to keep the HTML snippet up to date with the data it is bound to. In order to accomplish that, Angular has to check at certain points in time if data has changed. If you bind your HTML to the result of a function call, Angular has to execute the function to be sure that the HTML is up to date.
Now, these certain points in time are when Angular finishes $applying some code (the end of the $digest cycle).
Framework events, such as ng-click, causes Angular to $apply code.
If you are concerned with the non-relevant re-evaluation of your Effort, you should bound it to a $scope variable like $scope.effort.
EDIT:
You mentionned not to be using any events, so assuming initialDate and endDate won't be updated, the resulting effort won't need to be recomputed. You should then compute only once.
//ActivitieController
//code to call after $scope.activities gets filled (not quoted in your question)
$scope.activities.map(function(a){
a.effort = calculateEffortFromValues(a.initialDate,a.initialDate);
});
//HTML
<td>{{activityList.effort}}</td>
I don't really get why you would be using Angular or any client framework/library for an HTML page that doesn't require interactivity (== events).

AngularJS InfDig error (infinite loop) with ng-repeat function that returns array of objects

Here's my code:
<h1 ng-repeat="item in func()">something</h1>
$scope.func = function(){
return [{"property" : "value1"},{"property": "value2"}];
}
In Angular.js v. 1.1.1 there's no mistake. In Angular.JS v 1.2.1 I get an infDig mistake.
Fiddle of v.1.1.1
Fiddle of v.1.2.1
Could you explain this situation? Thanks a lot.
As of AngularJS 1.2: The "track by" expression was added to ng-repeat and more appropriately addresses this issue as demonstrated
in the following code.
<h1 ng-repeat="item in func() track by $index">something</h1>
$scope.func = function(){
return [{"property" : "value1"},{"property": "value2"}];
}
The following article helps understand the expression in more detail and why it is so useful, particularly when dealing with $$haskey Using Track-By With ngRepeat In AngularJS 1.2 by Ben Nadal.
The problem is that you're creating a new array each time, so it's something new that angular needs to track. As far as I can tell, ng-repeat runs, then immediately checks its collection again to see if anything changed in that cycle. Because the function returns a new array, that is perceived as a change.
Check this out: http://jsfiddle.net/kL5YZ/.
If you look in the console.log and click the button, you will see that the $$hashKey property of the objects is being changed each time ng-repeat runs.
The change occurs starting at version 1.1.4, but the changelog doesn't give any clues as to why the behavior is different. The new behavior does make more sense to me.
Here's a great post I found explaining the current behavior in depth: How to Loop through items returned by a function with ng-repeat?
If you make sure to return the same object/array each time, you won't have the error. You could have the function cache anything it creates based on the arguments and always return the same array/object when those arguments are passed in. So, myFunc('foo') will always return the same array, not a new one that looks the same. See the notes in my code below. Live demo (click).
<div ng-repeat="foo in foos">
<div ng-repeat="bar in barFunc(foo)">{{bar.text}}</div>
<div ng-repeat="bar in barFunc('test')">{{bar.text}}</div>
</div>
JavaScript:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
$scope.foos = [
'a','b','c'
];
//I put this into a service to avoid cluttering the controller
$scope.barFunc = myService.getObj;
});
app.factory('myService', function() {
/*
* anything created will be stored in this cache object,
* based on the arguments passed to `getObj`.
* If you need multiple arguments, you could form them into a string,
* and use that as the cache key
* since there's only one argument here, I'll just use that
*/
var cache = {};
var myService = {
getObj : function(val) {
//if we haven't created an array with this argument before
if (!cache[val]) {
//create one and store it in the cache with that argument as the key
cache[val] = [
{text:val}
];
}
//return the cached array
return cache[val];
}
};
return myService;
});

Resources