Two-way databind on a forEach? - javascript

I have an object of products. The products i get from a resource (Product), store in a factory (productsStore), and iterate through in a controller.
In another controller, i want to either empty of refresh products via productsStore.empty(); or productsStore.get();. But either of them does nothing. Meaning my forEach, below, is not being run again, and the list not updated.
What am i doing wrong?
Controller:
vm.products = productsStore.get();
vm.products.$promise.then(function (data) {
angular.forEach(vm.products, function (child) {
//. some code
)};
)};
Factory:
myApp.factory('productsStore', function ($http, $q, Product) {
var products = "";
var get = function () {
return products = Product.query();
};
var empty = function () {
return products = {};
};
// Bind products
products = get();
return {
get: get,
empty: empty
};
});
Resource:
myApp.factory('Product', function ($resource) {
return $resource('http://api.com/api/products/:id', { id: '#id' }, {
update: {
method: 'PUT'
}
});
});

try to loop through the data you get after the promise is resolved, otherwise it's a rather useless variable.
angular.forEach(data, function(child) {
//...do something with the child
});
Also, you have a typo, that I'm not sure you have in your actual code. In your controller, end of block should be }) and not )}

Related

How to pass param from controller to service in AngularJs

I'm currently working on a project to help me better understand angularjs! I am currently stuck on how to pass a parameter from the controller to service.
In my program, I have created a function called "GetForecastByLocation" when a user types in an input clicks on a button. From there I want to take their input and then pass it to the http call in service.js.
Originally, $http.get was in a long giant string of the API url, but I googled around and it seems that I'm supposed to use parameters when trying to change a portion of the string. As of right now, I know parameter is hardcoded to a specific city, but I want to take new input and pass the value of vm.city to the $http.get call.
If any one can help I would greatly appreciate it. Thank you!
controller.js
var app = angular.module('weatherApp.controllers', [])
app.controller('weatherCtrl', ['$scope','Data',
function($scope, Data) {
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);},
Data.getApps(city)
.then(function(data)){
//doing a bunch of things like converting units, etc
},
function(res){
if(res.status === 500) {
// server error, alert user somehow
} else {
// probably deal with these errors differently
}
}); // end of function
}]) // end of controller
service.js
.factory('Data', function($http, $q) {
var data = [],
lastRequestFailed = true,
promise;
return {
getApps: function() {
if(!promise || lastRequestFailed) {
promise = $http.get('http://api.openweathermap.org/data/2.5/weather?',{
params: {
q: Tokyo,
}
})
.then(function(res) {
lastRequestFailed = false;
data = res.data;
return data;
}, function(res) {
return $q.reject(res);
});
}
return promise;
}
}
});
Passing arguments to a factory method is no different than passing arguments to a plain old function.
First, set up getApps to accept a parameter:
.factory('Data', function($http, $q){
// ...
return {
getApps: function(city){
promise = $http.get(URL, {
params: {q: city}
}).then( /* ... */ );
// ...
return promise;
}
};
});
Then pass it your argument:
$scope.getForecastByLocation = function(myName) {
$scope.city = myName;
Data.getApps($scope.city);
}
It's just like setting a value to a function's context variable.
Services.js
Simple example of a service.
.factory('RouteService', function() {
var route = {}; // $Object
var setRoute_ = function(obj)
{
return route = obj;
};
var getRoute_ = function()
{
if(typeof route == 'string')
{
return JSON.parse(route);
}
return null;
};
return {
setRoute: setRoute_,
getRoute: getRoute_
};
})
Controllers.js
Simple example of Service usage:
.controller('RoutesCtrl', function ($scope, RouteService) {
// This is only the set part.
var route = {
'some_key': 'some_value'
};
RouteService.setRoute(route);
})

Loop through array?

In an Angular app, I have made a factory, where I get all my products and put them into a factory variable. From there I can get grab them, wherever I need them. So far s good.
I then want to manually loop through all the items in the products array. I have tried the following;
My factory first
myApp.factory('myStore', function ($http, $q, Store) {
var products = "";
// Bind products
products = Store.query();
return {
get: function () {
return products;
},
getSingle: function(id) {
},
set: function (data) {
products = data;
},
add: function (data) {
}
};
});
In my controller i have
// Bind products to list
vm.products = myStore.get();
console.log(vm.products);
for (var prop in vm.products) {
// returns just 2 objects?
console.log(prop);
}
for (i = 0; i < vm.products.length; i++) {
// Does not wok at all?
}
vm.products.forEach(function (child) {
// Does not work either. Simply does nothing.
});
What do I need to do to iterate through my array?
The returned products are in the correct format, and the correct values.
What gives?
The resource:
myApp.factory('Store', function ($resource) {
return $resource('http://api.com/api/products/:id', { id: '#id' }, {
update: {
method: 'PUT'
}
});
});
The output from console.log(vm.products): http://screencast.com/t/Q52EJ2F7gf
I changed the loop to
vm.positions.$promise.then(function(data)
and then it worked. So ti was a promise issues.. ofcourse.

Angular service issue -- returning an array in a service containing promises

As you can see this is my first time attempting this and I appear to be doing it incorrectly. I just want to take some code, consisting of promises and http requests, and put it in a service before the controller uses it. My goal is to simply clean up the controller so it doesn't contain all of that code.
After logging it in the last step of the controller the object appears as undefined. Also, all the requests are being made successfully. So, it's jumping through all the hoops fine so I'm guessing it must not be returning any value in the service and nothing gets passed on to the subsequent function in the controller. How can I return the 'people' array in the service after the promises have been fulfilled?
var myApp = angular.module('myApp', []);
myApp.controller('AppCtrl', function ($scope, $http, dataService) {
var getPeople = function () {
return $http.get('/getpeople');
};
getPeople().then(function (response) {
dataService.compilePeople(response)
})
.then(function (people) {
console.log(people);
$scope.people = people;
});
});
myApp.service('dataService', function ($q, $http) {
this.compilePeople = function (response) {
var people = [];
names = response.data;
grandPromiseArray = [];
names.forEach(function (index) {
var name = index,
count = $q.defer(),
skills = [],
urls = '/getskillsbyname/' + name,
urlc = '/getcountbyname/' + name;
grandPromiseArray.push(
$q.all([$http.get(urls), $http.get(urlc)])
.then(function (response) {
people.push({
name: name,
skills: response[0].data,
count: response[1].data
});
})
);
});
return $q.all(grandPromiseArray).then(function () {
return people
});
}
});
You need to return the promise from compilePeople() in order for the people to be passed into the next .then() handler. so close ;)
getPeople()
.then(function (response) {
//You were missing this return
return dataService.compilePeople(response)
})
.then(function (people) {
console.log(people);
$scope.people = people;
});

How to query and extract from server response in Angular

I want to create a find method that loops through an array returned by the $resource service in Angular.
If I have a service like so:
'use strict';
angular.module('adminApp').factory('ProductType', function($resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName){
var types = this.query(),
typeObject = {},
self = this;
for(type in types) {
var result = types[type],
resultName = self.normalizeName(result.name),
if(typeName === resultName) {
typeObject = result;
}
}
return typeObject;
};
return ProductType;
});
I tried wrapping it all in a function and returning the function thinking it had something to do with it being async and I also tried nesting a callback in the query method but that just allowed me to modify the response and not actually return anything differently.
When I try and set the return value to $scope in the controller I get a blank object
The this.query() method would return an array which might not be filled until the this.query() method has got its results back from the server. You will need to do something like this to wait until the call to the server has completed. As this is sort of async you will need to return a promise from this method that is resolved when the initial query has completed and you have searched the results.
'use strict';
angular.module('adminApp').factory('ProductType', [
'$q',
'$resource',
function($q, $resource) {
var ProductType;
ProductType = $resource('http://localhost:3000/api/v1/product_types/:id.json', {
id: '#id'
}, {
update: {
method: 'PUT'
}
});
ProductType.find = function(typeName) {
var defer = $q.defer(),
types = this.query(),
self = this;
types.$promise.then(function () {
var result,
resultName,
typeObject,
type;
for(type in types) {
result = types[type];
resultName = self.normalizeName(result.name);
if(typeName === resultName) {
typeObject = result;
break;
}
}
defer.resolve(typeObject);
}, function (err) {
// the called failed
defer.reject(err);
})
return defer.promise;
};
return ProductType;
}]);
Taken from the angular docs https://docs.angularjs.org/api/ngResource/service/$resource
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data.

Data flow issue with AngularJS

I have a function inside a directive that makes a query (and gets results, according to the console). The problem is that I can't seem to be able to store those results into a factory, and pass them to a controller.
This is the directive:
scope.getVersions = function(release) {
if (angular.isUndefined(release)) return;
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService = data.versions;
console.log(dataService);
});
};
The console shows that dataService contains an array with the results.
Then, I try to store it into a factory:
app.factory('dataService', [function(){
return { items: [] };
}]);
And I call it in a controller:
function VersionController($scope, dataService) {
$scope.versions = dataService.items;
console.log($scope.versions);
}
But both items and $scope.versions come back an empty array. Did I miss something?
You should really use a backing field to store that data, and use setter and geter functions for writing and reading respectively:
app.factory('dataService', [function(){
var _items={};
return {
setItems:function(value){
_items=value;
},
getItems:function(){
return _items;
}
};
}]);
And for setting the data:
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService.setItems(data.versions);
console.log(dataService);
});
and reading:
function VersionController($scope, dataService) {
$scope.versions = dataService.getItems();
console.log($scope.versions);
}
See demo plunk.
There's a misunderstanding of angular factories going on here. You're trying to set dataService = , which will never work.
As Mohammad mentioned, you need to have a variable set outside of your return value in the factory and the return value is basically an object with functions that allow you to manipulate your constant. So what you need is a getter "getItems()" for getting the items, and a setter "addItem(item)" for adding an item to your items array.
So you're never directly injecting your "items" into a controller, you're injecting a bunch of functions that can get or manipulate your "items".
scope.getVersions = function(release) {
if (angular.isUndefined(release)) return;
musicInfoService.getReleaseVersions(release.id)
.success(function(data) {
dataService.addItem(data.versions);
console.log(dataService.getItems());
});
};
app.factory('dataService', [function(){
var items = [];
return {
addItem: function(item) {
items.push(item);
},
getItems: function() {
return items;
}
};
}]);
function VersionController($scope, dataService) {
$scope.$watch(dataService.getItems, function(items) {
$scope.versions = items;
console.log($scope.versions);
});
}

Categories