AngularJS scope issue with $http - arrays

Continuing my adventures with AngularJS, I have what I believe is a scope issue, but not entirely sure.
In my controller I'm building two test array's for demo purposes, search.filterDemo and search.filter (real data).
Controller
app.controller('searchBtnCtrl', ['$scope', '$http', function ($scope, $http) {
var search = this;
search.filter = {};
search.results = [];
search.resultsDemo = [];
search.keywordsToFilter = [
{ name: 'itemA' },
{ name: 'itemB' },
{ name: 'itemC' },
{ name: 'itemD' }
];
$scope.performSearch = function () {
search.resultsDemo = [
{ name: 'itemA', content: 'This is demo content' },
{ name: 'itemB', content: 'This is demo content' },
{ name: 'itemC', content: 'This is demo content' },
{ name: 'itemD', content: 'This is demo content' }
];
$http.get('http://localhost/content').then(function (response) {
search.resultCount = response.data.response.docs.length;
for (var i = 0; i < search.resultCount; i++) {
search.results.push({ name: search.results[i]._name[0], content: search.resultsTemp[i]._content[0]});
}
console.log(search.results);
});
console.log(search.resultsDemo);
console.log(search.results);
}
}]);
Output from the console log for search.resultsDemo is as I expect it to be:
Array [ Object, Object, Object, Object, Object, Object, Object, Object ]
Then if I click on the Array link I see that the array has 8 items
Array[8]
This is all correct to me, my first array is keeping it's scope.
However my second array is not quite.
Output from the second array is as follows:
Inside the $http call it displays properly -
Array [ Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, 3 moreā€¦ ]
However the second console.log of the array just 3 lines later displays as:
Array[ ]
And if I click on the array link it shows:
Array[0]
The interesting thing is that the data is in there, it's just not showing up as objects like the first array (I can click the array link and I do see the objects listed under the Array[0] in the console, and I can twirl them open to see the data.
So is this a scope issue, or is it something else related to the async nature of Angular that's not formatting my second (real) array correctly?
It's driving me batty, as I need to have my second array formatted properly like the first array for processing later in the function.
thanks!

That is because your second console.log is outside the scope of your then statement. When your second console.log runs, the data likely has not been returned yet.
In fact, I would wager to say that your second console.log(search.results) likely ends up in your console before your first console.log(search.results) does.

Related

Angularjs : How to retrieve data from ng-options in angularjs using single string or only one loop

good day, i would like to ask question about retrieving selected data from ng options using string as comparator.
in my controller i have this data
$scope.myList = [
{ id: 0, name: "foo" },
{ id: 1, name: "foo1" },
{ id: 2, name: "foo2" }
];
for my index.html
<select
ng-options="list.name for listin myList"
ng-model="selectedList"
/select>
so the scenario is this, i have my own way of retrieving data but it includes nested looping and my question is, is there any way's to do this just using one loop or a one liner code? because basically i already have the value from database, is there any way so that i don't need to iterate the whole list just to show the data in HTML select input or if not possible at least i wont use nested loops, thanks
this is what i tried so far
// assume the value retrieved from database is "foo1"
var retrievedData = "foo1";
for(var i=0; i<myList.length; i++) {
if(myList[i]['name'] == retrievedData ) {
$scope.selected = myList[i];
}
}
You can do this in one line like bellow
var retrievedData = "foo1";
$scope.selected = myList.find((ele) => ele['name'] == retrievedData);
find() will return the first match.
You can do this in one line like bellow
myList.filter(function(listitem,listitemid){return listitem.name==retrievedData});

Angularjs watch array and get changed object

In the context of inserting or deleting from an array in angular, is it possible to watch the array and then get the object that was added or deleted from the array? I don't care about the objects properties in the array, only the objects themselves being added or deleted. So I believe $watchCollection is a good fit here so it's not a deep watch.
For example, I have this array as a model for a dual list box:
$scope.employees = [
{
name: "Bob",
id: "0"
},
{
name: "Carl",
id: "1"
},
{
name: "Bill",
id: "2"
}
];
The listbox will automatically update $scope.employees when i move one off of it or onto it (insert/delete). If I do:
$scope.$watchCollection('employees', function(){
//somehow get changed object
var changedObject = ...;
$scope.changedItems.push(changedObject);
});
I want to be able to get the added/deleted item so I can use it or save it somewhere.
The $watchCollection handler function receives both new and old value:
$scope.$watchCollection('employees', function(newValue, oldValue){
console.log(newValue);
console.log(oldValue);
var addedArray = newValue.filter(x => !oldValue.find(x));
var removedArray = oldValue.filter(x => !newValue.find(x));
var changedObject = {added: addedArray, removed: removedArray};
$scope.changedItems.push(changedObject);
});
For more information, see AngularJS $watchCollection API Reference

Array to multiple object in underscore

I have an array that looks like this:
[{
LocalBond:"0",
LocalCash:"2.42",
LocalEquity:"0",
ForeignEquity: "4",
...
}]
What I want it look like:
[{
Source: "LocalBond",
Value: "0"
},
Source: "LocalCash",
Value: "2.42"
},
Source: "LocalEquity",
Value: "0"
},
{...}
]
I want to turn a single object into many objects. I also need the exclude the 'ForeignEquity' result.
I tried using _.map, and returning the fields I want, but I am struggling a bit. Am I on the right track? When I pass more than one parameter into my function, I don't get the desired result.
The most simple code is pure javascript:
Using for..in access to the property of the object, and inside of the for loop build the array.
http://www.w3schools.com/jsref/jsref_forin.asp
Example:
https://jsfiddle.net/jewrsL8a/5/
var collection = [{
LocalBond:"0",
LocalCash:"2.42",
LocalEquity:"0",
ForeignEquity: "4"
}];
var result = [];
for (var property in collection[0]) {
if(property!=='ForeignEquity'){
result.push({'Source': property, 'Value': collection[0][property]});
}
}
console.log(result);

Angular ng-repeat with constant & Restangular

I'm working with a weird data model (no way around it at this point). I'm using restangular to make a rest call to get back a single resource object
Normally, the resource object returned by restangular is just whatever I set my
$scope.resource = response to and I can do resource.name , resource.id in the view/template, etc..
Except this group of resources instead of returning the key, value pairs in the response object, it returns an object within an object like so
resource1: {name: 'value', stuff: 'value', etc}
which is fine because then I would just set my $scope.resource = response.resource1 in my controller
except the problem is, is that there's 5 different kind of resource object names so if I make a resource by id call I might get back resource2, resource4, resource1, etc. so setting my $scope.resource = response.resource1 would only work when I get resource1.
My first attempt to solve this was to just use ng-repeat in which I set
<ul ng-repeat="(resource, value) in resource">
<li class="list-group-item">
Name:
<span class="pull-right"> {{ resource.name }} </span>
</li>
</ul>
which works great except because restangular returns all this extra stuff it's looping through each object it's repeating a bunch of blank html stuff if that makes sense.
My other thought was to try making a constant and make an object that has all 5 resources there and my ng-repeat would only populate based off that constant object (ie: it would check for those strings "resource1, resource2, etc" and if it's there then it will populate the template. But I'm not exactly sure how to do this.
Any other options or are there ng-repeat features i'm just not utilizing? any Help thanks
Here's the example I will be working from. Initially your incoming data looks something like this I believe...
$scope.data = [
{
resource1 : { name: 'r1' }
},
{
resource2 : { name: 'r2' }
},
{
resource2 : { name: 'r2' }
}];
When you receive the data you can normalize it by flattening it out into the following structure...
$scope.normalized = [
{ name : 'r1' },
{ name : 'r2' },
{ name : 'r2' }
];
Or you can add a common field for the object "type"
$scope.expanded = [
{
type : 'resource1',
resource1 : { name: 'r1' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
}];
Or you can normalize but retain type data...
$scope.normalizedType = [
{ type : 'resource1', name : 'r1' },
{ type : 'resource2', name : 'r2' },
{ type : 'resource2', name : 'r2' }
];
Normalizing upon retrieval of the data is probably your best bet. The question then becomes do you need to retain the objects type information.
So another solution I came up with was put all the resource key names into a list
resources = ['resource1', 'resource2', 'resource3', 'etc..']
and in my restangular service promise I just checked for which resource number it would be with a for loop like this
return ResourceRestangular.all('resource').get(resourceId).then(function(response){
for (i = 0; i < resources.length ; i++){
if (resources[i] in response){
self.resource = response[resources[i]];
}
}
return self.resource;
No need for ng-repeat anymore!

AngularJS changing deep scope object value

Hit a bit of brick wall here so hoping for some guidance.
I'm building up a scope variable called 'display' which is built off 2 http calls which I haven't included to keep it simple. Basically all i'm doing is adding product types to the each category object.
$scope.display = {};
portal.fetchCategories().then(function(data) {
$scope.categories = data.categories;
return portal.fetchProductTypes();
})
.then(function(data) {
$scope.productTypes = data.product_types;
angular.forEach($scope.categories, function(value) {
$scope.display[value.id] = {
title: value.title,
start: value.start,
end: value.end,
product_types: $scope.productTypes
};
});
})
This all works fine.
The trouble I'm having is when I target a product type inside a category like this and attempt to update the title:
$scope.display[2].product_types[0]['title'] = "Updated Title";
It is actually updating the product type title in all the categories rather than just specified category. I suspect it's just updating $scope.productTypes.
Can anyone shed any light on what I'm doing wrong?
Problem is that the same object reference is associated with all product types for display. You can overcome this by making a copy of productTypes, if that suffices and not a problem.
angular.forEach($scope.categories, function(value) {
$scope.display[value.id] = {
title: value.title,
start: value.start,
end: value.end,
product_types: angular.copy($scope.productTypes)
};
});

Resources