foreach loop in angularjs - angularjs

I was going through the forEach loop in AngularJS. There are few points that I did not understood about it.
What is the use of the iterator function? Is there any way to go without it?
What is the significance of the key and value as shown below?
angular.forEach($scope.data, function(value, key){});
PS: I tried to run this function without the arguments and it did not work.
Here's my json:
[
{
"Name": "Thomas",
"Password": "thomasTheKing"
},
{
"Name": "Linda",
"Password": "lindatheQueen"
}
]
My JavaScript file:
var app = angular.module('testModule', []);
app.controller('testController', function($scope, $http){
$http.get('Data/info.json').then(
function(data){
$scope.data = data;
}
);
angular.forEach($scope.data, function(value, key){
if(value.Password == "thomasTheKing")
console.log("username is thomas");
});
});
Another question: Why the function above does not enter on if condition and print "username is thomas" in the console?

Questions 1 & 2
So basically, first parameter is the object to iterate on. It can be an array or an object. If it is an object like this :
var values = {name: 'misko', gender: 'male'};
Angular will take each value one by one the first one is name, the second is gender.
If your object to iterate on is an array (also possible), like this :
[{ "Name" : "Thomas", "Password" : "thomasTheKing" },
{ "Name" : "Linda", "Password" : "lindatheQueen" }]
Angular.forEach will take one by one starting by the first object, then the second object.
For each of this object, it will so take them one by one and execute a specific code for each value. This code is called the iterator function. forEach is smart and behave differently if you are using an array of a collection. Here is some exemple :
var obj = {name: 'misko', gender: 'male'};
var log = [];
angular.forEach(obj, function(value, key) {
console.log(key + ': ' + value);
});
// it will log two iteration like this
// name: misko
// gender: male
So key is the string value of your key and value is ... the value. You can use the key to access your value like this : obj['name'] = 'John'
If this time you display an array, like this :
var values = [{ "Name" : "Thomas", "Password" : "thomasTheKing" },
{ "Name" : "Linda", "Password" : "lindatheQueen" }];
angular.forEach(values, function(value, key){
console.log(key + ': ' + value);
});
// it will log two iteration like this
// 0: [object Object]
// 1: [object Object]
So then value is your object (collection), and key is the index of your array since :
[{ "Name" : "Thomas", "Password" : "thomasTheKing" },
{ "Name" : "Linda", "Password" : "lindatheQueen" }]
// is equal to
{0: { "Name" : "Thomas", "Password" : "thomasTheKing" },
1: { "Name" : "Linda", "Password" : "lindatheQueen" }}
I hope it answer your question. Here is a JSFiddle to run some code and test if you want : http://jsfiddle.net/ygahqdge/
Debugging your code
The problem seems to come from the fact $http.get() is an asynchronous request.
You send a query on your son, THEN when you browser end downloading it it execute success. BUT just after sending your request your perform a loop using angular.forEach without waiting the answer of your JSON.
You need to include the loop in the success function
var app = angular.module('testModule', [])
.controller('testController', ['$scope', '$http', function($scope, $http){
$http.get('Data/info.json').then(function(data){
$scope.data = data;
angular.forEach($scope.data, function(value, key){
if(value.Password == "thomasTheKing")
console.log("username is thomas");
});
});
});
This should work.
Going more deeply
The $http API is based on the deferred/promise APIs exposed by the $q
service. While for simple usage patterns this doesn't matter much, for
advanced usage it is important to familiarize yourself with these APIs
and the guarantees they provide.
You can give a look at deferred/promise APIs, it is an important concept of Angular to make smooth asynchronous actions.

you have to use nested angular.forEach loops for JSON as shown below:
var values = [
{
"name":"Thomas",
"password":"thomas"
},
{
"name":"linda",
"password":"linda"
}];
angular.forEach(values,function(value,key){
angular.forEach(value,function(v1,k1){//this is nested angular.forEach loop
console.log(k1+":"+v1);
});
});

The angular.forEach() will iterate through your json object.
First iteration,
key = 0, value = { "name" : "Thomas", "password" : "thomasTheKing"}
Second iteration,
key = 1, value = { "name" : "Linda", "password" : "lindatheQueen" }
To get the value of your name, you can use value.name or value["name"]. Same with your password, you use value.password or value["password"].
The code below will give you what you want:
angular.forEach(json, function (value, key)
{
//console.log(key);
//console.log(value);
if (value.password == "thomasTheKing") {
console.log("username is thomas");
}
});

In Angular 7 the for loop is like below
var values = [
{
"name":"Thomas",
"password":"thomas"
},
{
"name":"linda",
"password":"linda"
}];
for (let item of values)
{
}

Change the line into this
angular.forEach(values, function(value, key){
console.log(key + ': ' + value);
});
angular.forEach(values, function(value, key){
console.log(key + ': ' + value.Name);
});

Related

AngularJs - keeps getting old value of scope even if it is updated

In my application, I'm retrieving some fields from the database and setting the values in local storage when a user logs in.
and then retrieving from local storage to display it to user:
if (localStorage.getItem('a') != undefined) {
$rootScope.a = localStorage.getItem('a');
}
So this is working fine. But the problem is when the value gets updated in the database and user logs in after logging out, then even if the local storage has correct value (i.e., recently updated value), the first time it will display the old value of the scope variable which just got updated.
I tried $apply() and also $digest() as suggested in different posts here :
$timeout( function () {
$scope.$apply( function () {
$rootScope.a = localStorage.getItem('a');
});
});
But it didn't work out. It always displays the old value of scope.
It will only display the new value after reloading the page once.
P.S. - The web page in my application won't be reloaded in any module, even when logging in and out.
You can try watching for the scope variable like this:
$rootScope.$watch('a', function (newVal, oldVal){
if newVal != oldVal
$rootScope.a = newVal;
}
Something else to try is to change 'a' from string to object as I think that angular watches for values using object reference.
here's some useful reference for $watch
http://www.learn-angular.org/#!/lessons/watch
https://www.bennadel.com/blog/2852-understanding-how-to-use-scope-watch-with-controller-as-in-angularjs.htm
Hope it helps in any way
EDIT
ok I tested it. You don't need watch neither $apply if you refresh the scope when data refreshing.
Here's what I've done:
(function() {
angular.module('myapp', []).controller('myctrl', [
'$scope', function($scope) {
var data, getDataFromLocalStorage;
console.log("scope is ", $scope);
getDataFromLocalStorage = function() {
return JSON.parse(localStorage.getItem('data'));
};
data = [
{
id: 1,
text: "test1"
}, {
id: 2,
text: "test2"
}, {
id: 3,
text: "test3"
}
];
localStorage.setItem('data', JSON.stringify(data));
$scope.myData = getDataFromLocalStorage();
return $scope.changeData = function() {
var dataNew;
dataNew = [
{
id: 4,
text: 'text4'
}, {
id: 5,
text: 'text5'
}, {
id: 6,
text: 'text6'
}
];
localStorage.setItem('data', JSON.stringify(dataNew));
return $scope.myData = getDataFromLocalStorage();
};
}
]);
}).call(this);
https://codepen.io/NickHG/pen/rzvGGx?editors=1010

angular material md-autocomplete dynamic loading issue

I'm using Angular Material component "md-autocomplete" in my project.
We are trying to render dynamic response that we are getting from "md-list" component's item-click's event call.
Issue: However, before the event call is invoked, md-autocomplete method is invoked.
My Requirement: Is there a way to invoke the event call before invoking md-autocomplete method.
Here we attached sample images, that show's you basic response what we need as output.
I tried below code, But it is not working. I need solution for the problem.
HTML Source Code:
md-list code
<md-list>
<div ng-repeat="object in ['object 1', 'object 2', 'object 3', 'object 4', 'object 5', 'object 6'] track by $index">
<md-list-item class="md-2-line contact-item" ng-click="listItemClick($event, object)">
<div class="md-list-item-text compact">
<h3>Object data displayed here like object 1, object 2 and etc ...</h3>
</div>
</md-list-item>
</div>
</md-list>
md-autocomplete code :
<md-autocomplete
ng-disabled="isDisabled"
md-no-cache="noCache"
md-selected-item="selectedItem"
md-search-text-change="searchTextChangeEvent(searchText)"
md-search-text="searchText"
md-selected-item-change="selectedItemChangeEvent(item)"
md-items="item in search(searchText)"
md-item-text="item.id"
md-min-length="0"
placeholder="Search by id ..."
md-menu-class="autocomplete-custom-template">
<md-item-template>
<span class="item-title">
{{id}}
</span>
</md-item-template>
</md-autocomplete>
AngularJS Script Code :
(function() {
var app = angular.module('module-name');
var controller = function($scope, $rootScope,$http, $timeout, $q, $log) {
var self = this;
self.simulateQuery = false;
self.isDisabled = false;
$rootScope.objectName = "object 1";
self.response = loadValues($rootScope.objectName);
self.search = search;
self.selectedItemChangeEvent = selectedItemChangeEvent;
self.searchTextChangeEvent = searchTextChangeEvent;
// ******************************
// Internal methods
// ******************************
/**
* Search for repos... use $timeout to simulate
* remote dataservice call.
*/
function search (query) {
var results = query ? self.response.filter( createQueryFilterFor(query) ) : self.response,
deferred;
if (self.simulateQuery) {
deferred = $q.defer();
$timeout(function () { deferred.resolve( results ); }, Math.random() * 1000, false);
return deferred.promise;
} else {
return results;
}
}
function searchTextChangeEvent(text) {
$log.info('Text changed to ' + text);
}
function selectedItemChangeEvent(item) {
$log.info('Item changed to ' + JSON.stringify(item));
}
/**
* Build `components` list of key/value pairs
*/
function loadValues(name) {
var dynamicData = '';
if(name === "object 1") {
dynamicData = [{
"id": 1,
"name": "some name here"
},{
"id": 2,
"name": "some name here"
}];
} else if(name === "object 2") {
dynamicData = [{
"id": 3,
"name": "some name here"
},{
"id": 4,
"name": "some name here"
}];
} else if(name === "object 3") {
dynamicData = [{
"id": 5,
"name": "some name here"
},{
"id": 6,
"name": "some name here"
}];
}
return dynamicData.map( function (response) {
response.value = response.id.toLowerCase();
return response;
});
}
/**
* Create filter function for a query string
*/
function createQueryFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.value.indexOf(lowercaseQuery) === 0);
};
}
$scope.listItemClick = function(event, object) {
$rootScope.objectName= object.someFiledName; // It will give md-list-item name (like object 1 or object 2 and etc ...)
self.response = loadValues($rootScope.name);
}
};
app.controller("controller", controller)
}());
I have taken your code and created a plunkr demo. I tweaked it few places, please feel free to explore the code in plunkr. Now suggestions are loading according to object selected.
I think filter logic was not correct according to use case you mentioned here so I corrected createQueryFilterFor function logic. I made changes in md-item-template section too.
function createQueryFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
//below condition updated to match search id
return (item.value.toString() === lowercaseQuery);
};
}
see demo
I've fixed some issues in your code and corrected the hovering and double-clicking issue as per your comment in another answer.
Here's your updated code: http://codepen.io/anon/pen/ryXyxj
Note to communicate between controllers it is considered better practise to use a shared service rather than setting values on the $rootScope:
app.controller('mainController', function($scope, menuSelection) {
$scope.menuSelection = menuSelection; // retrieve settings object from service method and bring into scope
// now whenever one sets $scope.menuSelection.selected = "object 2", it will update the value in the other controller as well (and vice-versa)
});
app.controller('secondController', function($scope, menuSelection) {
$scope.menuSelection = menuSelection; // retrieve settings object from service method and bring into scope
});
app.factory('menuSelection', function() {
var settings = {};
settings.selected = 'Object 1'; // default
return settings;
});
You can find a demo of 2 controllers communicating via a service here: https://stackoverflow.com/a/42408380/1544886

Why is Angular http.post converting string value to an array?

I have an Angular JS and Laravel app. One of my form feilds is an ng-options driven select. The values are strings but when I post using http.post, one of the values seems to be getting converted to an array.
I have this in my JS controller for the options;
$scope.periods = [
{label: '1'},
{label: '2'},
{label: '3'},
{label: 'OT'}
];
This is my view for the select;
<select ng-model="egoal.period"
ng-options="period.label as period.label for period in periods"
class="form-control game-control"
required>
</select>
And then this in the controller to post;
$scope.changeGoal = function() {
var egoal = this.egoal;
$http.post('api/editGoal/' + this.goal.id, egoal)
.success(function () {
$scope.egoal = {};
});
};
Oddly, when I dump the $request, I get period => [] and then this error;
ErrorException in helpers.php line 685:
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
Been looking at this for a long time now. What could be causing this?
Thanks!
UPDATE:
Egoal object looks like this after the selection is made;
EGoal: {
"id": 268,
"game": 147,
"season": 4,
"team_id": 2,
"period": "2",
"min": 7,
"sec": 54,
"scorer": 11,
}
but the period value is converted on post to an empty array...
$scope.changeGoal = function() {
var egoal = this.egoal;
$http.post('api/editGoal/' + this.goal.id, egoal)
A function in javascript is a block with its own scope. You need to use the vm concept. https://github.com/johnpapa/angular-styleguide/tree/master/a1#controlleras-with-vm
In the context of the changeGoal function, this.egoal is undefined.
Sending undefined as data to $http.post somehow serializes it to an empty array.
From the ngOptions docs:
When an item in the menu is selected, the array element or object property represented by the selected option will be bound to the model identified by the ngModel directive.
So, when you select one of the items from the dropdown, your ngModel egoal.period is not 1, or 2, or 3, or OT, it's {label: '1'}, {label: '2'}, etc. It's actually a pointer to the one of the items in your $scope.periods array.
Check out this plunker to get a better idea of what I mean:
http://embed.plnkr.co/9cJ2Hy/
Your PHP script is probably expecting a json object like this:
{
...
"period": '1',
...
}
But it's actually getting something more like this:
{
...
"period": "{\"label\": '1'}",
...
}
AngularJS uses JSON object by default in $http.post .
You can change the default setting by adding a correct Content-Type header to the config object:
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
UPDATE:
... by simply adding:
angular.module('myApp', [])
.config(function ($httpProvider) {
$httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded';
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
})
Or another solution would be:
return $http({
method: 'POST',
url: "/yoururl/",
data:{
'param1': "Test",
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}}).then(function(result) {
console.log(result);
}, function(error) {
console.log(error);
});

Firebase retrieve data shows only one result

I am trying to retrieve a data of objects and the result shows on the console but it only shows one result(the last result) on the page.
$http.get('/users').success(function(data) {
$scope.username = data; //assign data to username
console.log(data);
});
And in my Jade template, i have
section(data-ng-controller='browse')
h4 hi
ul(data-ng-repeat='user in username')
li {{user}}
I want to loop through this data
{
"google: xxxxxxxxxxxxx" : {
"name" : "ZZZ YYY",
"profile" : {
"briefProfile" : "Cost call",
"career" : "Web Developer",
"projects" : {
"-JjtSgiwkqFxTxMv0Gip" : "http://zipper.com"
}
},
"provider" : "google"
},
"google:xxxxxxxxxxx" : {
"name" : "SSS TTT",
"profile" : {
"briefProfile" : "Desired Intelligence",
"career" : "Consultant"
},
"provider" : "google"
}
}
Thanks.(i'm not using angularFire).
If you're using the $http service, then you're not using Firebase.
You are repeating the ul instead of the li. Also, {{user}} will be the whole user object, and you will see something like [Object object] on your page instead of a string. So call {{user.name}}.
ul
li(data-ng-repeat='user in users') {{user.name}}
Assuming that $http is there by mistake, and you meant to use Firebase instead, you probably want to start by reviewing the Firebase Getting Started guide
// Get a reference to users
var ref = new Firebase('https://<YOUR-FIREBASE>.firebaseio.com/users');
// Attach an asynchronous callback to read the data at users ref
ref.on("value", function(snapshot) {
console.log(snapshot.val());
$scope.users = snapshot.val();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
If you're using AngularFire you should take a look at this:
Instead of using $http.get try:
var data = $firebaseArray(new Firebase("URL"));
$scope.data = data;
Don't forget to add $firebaseArray as a dependency in your controller.
More info on AngularFire docs:
https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray
If you're not using AngularFire take a look at this:
var firebaseRef = new Firebase('YOUR-FIREBASE-URL/users/');
firebaseRef.on('value', function(dataSnapshot) {
var data = dataSnapshot.val();
console.log(data);
$scope.users = data;
// Might need to use $digest to update $scope.
$scope.$digest();
});
More about on() in Firebase docs:
https://www.firebase.com/docs/web/api/query/on.html

angularjs + jsonp returns a fail

I am trying to build a sample app that will grab data using JSONP to populate. I have it put up at http://angular.onagrag.com/ and clicking on register.
The file I am trying to load is at http://api.onagrag.com/data.json
When I access http://angular.onagrag.com/register it fires the error method of the object (and it fires it twice)
here is the angular js file that I am using (it is also located at http://angular.onagrag.com/js/test.js
It runs fine if I use local data (e.g. use the $http.get method instead of the $http.jsonp method), but will not work with jsonp. Any help is appreciated!
var App = angular.module('popdust', ['ngResource']).config(['$locationProvider', function($location) {
$location.html5Mode(true).hashPrefix('!')
}]);
App.config(['$routeProvider', function($routes) {
$routes.when('/register',{
templateUrl : '/templates/register.html',
controller : RegisterCtrl
});
$routes.when('/',{
templateUrl : '/templates/home.html',
controller : HomeCtrl
});
}]);
var HomeCtrl = function($scope, $http, $location) {
$scope.title = 'We are home';
$scope.obj = ['one', 'two','three'];
};
var RegisterCtrl = function($scope, $http, $location) {
$scope.title = 'Register!';
$scope.handleData = function(data){
$scope.fields = data;
}
$scope.fetchjsonp = function(){
$http.jsonp('http://api.onagrag.com/data.json?callback=JSON_CALLBACK').success(function(data){
alert("success");
}).error(function(data, status, headers, config) {
alert("YOU FAIL");
});
}
$scope.fetch = function(){
$http.get('js/data.json').success($scope.handleData);
}
$scope.fetchjsonp();
};
HomeCtrl.$inject = ['$scope','$http','$location'];
RegisterCtrl.$inject = ['$scope','$http','$location'];
Looks to me like the problem is with your resource. When I check http://api.onagrag.com/data.json?callback=JSON_CALLBACK I get the following response:
[{
"id" : "first_name",
"title" : "First Name",
"description" : "The name your parents gave you"
},{
"id" : "last_name",
"title" : "Last Name",
"description" : "In Spanish, it's called your apellido (or something like that)"
}]
This is not a valid JSONP response. With the request parameter callback=nameOfCallbackFn the response should be a single function call to a function named nameOfCallbackFn (with the result as it's only parameter).
Update: The server that serves the JSONP must read the callback request parameter and respond with a file that does a method call to the request method name. When you use the angular $http.jsonp method, angular will change the callback request parameter to the correct angular jsonp callback method name (atm they seem to be named angular.callback._0, ..._1 etc). You can't serve a static file as this name might change from one request to the other. This was not clear in my original answer.
Something like this:
nameOfCallbackFn ( [{
"id" : "first_name",
"title" : "First Name",
"description" : "The name your parents gave you"
},{
"id" : "last_name",
"title" : "Last Name",
"description" : "In Spanish, it's called your apellido (or something like that)"
}] );
Where nameOfCallbackFn is specified by angular.
JSONP has some potential security vulnerabilities - you can read more about them and how to prevent them in your angular app here.

Resources