I'm using ngResource to get the data from the server side into my view, and I have a custom action that I defined that gets items count, it looks like this in the code (controller):
$scope.totalItems = Item.item_count().total
And in my view :
This customer bought {{totalItems}}
In my view I just get the This customer bought without other part being interpolated. However also when I do this :
console.log(Item.item_count().total)
console.log(Item.item_count())
I get the undefined for first statement but for second one I get this :
Why am I unable to access the total count from the view, and what do I do to fix this?
Update (service code) :
app.factory('Item', function($resource) {
return $resource(
window.config.API_URL + '/:apiPath/:id', {
id: '#id',
apiPath: '#apiPath'
}, {
query: {
method: 'GET',
params: {
apiPath: 'items'
},
isArray: true
},
item_count: {
method: 'GET',
params: {
apiPath: 'item_count'
},
isArray: false
}
}
);
});
You need to get the $promise and pass in a callback like so:
Item.item_count().$promise.then(function(data) {
$scope.totalItems = data.total;
});
If you need to have the data resolved before loading your view, you can use $stateProvider to resolve it. This should give you a good idea how and some implications of using it: http://www.codelord.net/2015/06/02/angularjs-pitfalls-using-ui-routers-resolve/.
look like you get a promise returned.
Try calling console.log(Item.item_count().then(function(data) {
console.log(data);
});
Without seeing your other code my guess is the line
$scope.totalItems = Item.item_count().total is getting resolved before the promise is getting resolved. That line should be in sort of then catch for the promise. However this is just a guess since you haven't actually posted this code.
Because Item.item_count() returns a promise you have to wait until it is resolved before you set $scope.totalItems:
Item.item_count().then(function(item_count) {
$scope.totalItems = item_count.total;
});
Try removing the scope. from your {{}} in the view.
This customer bought {{totalItems}}
Related
I have a problem with angular-ui typeahead component. It does not show values populated by angular resources, however using $http works well. I suppose I missing some trick here with asycn call and correct population of returned values.
Working code
$scope.searchForContact = function(val) {
return $http.get('/api/contacts/search', {
params: {
q: val
}
}).then(function(response){
return response.data.map(function(item){
return item.name;
});
});
};
Not working code
$scope.searchForContact = function(val) {
return Contact.search({q: val}, function(response){
return response.map(function(item){
return item.name;
});
});
});
...
'use strict';
app.factory("Contact", function($resource, $http) {
var resource = $resource("/api/contacts/:id", { id: "#_id" },
{
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'search': { method: 'GET', isArray: true, url: '/api/contacts/search', params: true },
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
return resource;
});
Pug template code
input.form-control(
type='text'
ng-model='asyncSelected'
uib-typeahead='contact for contact in searchForContact($viewValue)'
typeahead-loading='loadingLocations'
typeahead-no-results='noResults'
)
i.glyphicon.glyphicon-refresh(ng-show='loadingLocations')
div(ng-show='noResults')
i.glyphicon.glyphicon-remove
|
|No Results Found
Angular resources are working fine, including search endpoint - I just output on page result returned by the search endpoint. In both results should be just an array with string values. What am I doing wrong?
The difference between $http.get and your Contact.search is that the first one returns a promise and the latter doesn't. Any $resource method will usually be resolved to the actual response. I'll show that with an example.
Getting data with $http
var httpResult = $http.get('http://some.url/someResource').then(function(response) {
return response.map(function(item) { return item.name });
});
The httpResult object contains a promise, so we need to use then method to get the actual data. Moreover, the promise will be resolved to the mapped array, which is the expected result.
Getting data with $resource
var someResource = $resource('http://some.url/someResource');
var resourceResult = someResource.query(function(response) {
return response.map(function(item) { return item.name });
});
The resourceResult isn't a promise here. It's a $resource object which will contain the actual data after the response comes from the server (in short, resourceResult will be the array of contacts - the original, not mapped, even though there is a map function). However, the $resource object contains a $promise property which is a promise similar to one returned by $http.get. It might be useful in this case.
Solution
I read in documentation that in order to make uib-typehead work properly, the $scope.searchForContact needs to return a promise. Instead of passing the callback function to search, I would simply chain it with the $promise from $resource object to make it work.
$scope.searchForContact = function(val) {
return Contact.search({q: val}).$promise.then(function(response){
return response.map(function(item){
return item.name;
});
});
});
Let me know if it works for you.
I am using ngResource in combination with ng-repeat and noticed that slow REST calls doesn't update the list properly. It keeps empty.
As far as I understood I need a binding between controller and ng-repeat element.
My resource and controller definition:
(function (configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource, REST_CONFIG) {
return {
Technical: $resource(REST_CONFIG.baseUrl + REST_CONFIG.path + '/', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function (configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
loggingResource.Technical.query(function (data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
ng.repeat usage:
<tr class="table-row" ng-repeat="log in logController.logs">
What I have tried so far:
ng-bind in combination with ng-repeat
$q with deferrer
$promise of ngResource
What did I miss?
My try to get it on plnkr: https://plnkr.co/edit/t1c5Pxi7pzgocDMDNITX
The {{}} provides a default binding between your controller and view and you don't need to add anything explicitly. I have updated your plunkr with some minor changes to injected constants etc. and it is working.
// Code goes here
angular.module("loggingModule", ['ngResource']);
(function(configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource) {
return {
Technical: $resource('https://api.github.com/users/addi90/repos', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function(configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
that.logs = [{
title: 'test2'
}];
loggingResource.Technical.query(function(data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
Since the api resource was not working, I have used my github repo api link there
The updated working plunkr is available here: https://plnkr.co/edit/cederzcAGCPVzc5xTeac?p=preview
Try to use
that.logs.push(data)
so you prevent to reinitialise the logs list. If you override the logs list and the ng-repeat is initialised before the logs list it seems to not be resolved.
Your logs list is set correctly after the rest call?
I've been trying to make a request to a NodeJS API. For the client, I am using the Mithril framework. I used their first example to make the request and obtain data:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://localhost:3000/store/all"});
}
};
var Component = {
controller: function() {
var stores = Model.getAll();
alert(stores); // The alert box shows exactly this: function (){return arguments.length&&(a=arguments[0]),a}
alert(stores()); // Alert box: undefined
},
view: function(controller) {
...
}
};
After running this I noticed through Chrome Developer Tools that the API is responding correctly with the following:
[{"name":"Mike"},{"name":"Zeza"}]
I can't find a way to obtain this data into the controller. They mentioned that using this method, the var may hold undefined until the request is completed, so I followed the next example by adding:
var stores = m.prop([]);
Before the model and changing the request to:
return m.request({method: "GET", url: "http://localhost:3000/store/all"}).then(stores);
I might be doing something wrong because I get the same result.
The objective is to get the data from the response and send it to the view to iterate.
Explanation:
m.request is a function, m.request.then() too, that is why "store" value is:
"function (){return arguments.length&&(a=arguments[0]),a}"
"stores()" is undefined, because you do an async ajax request, so you cannot get the result immediately, need to wait a bit. If you try to run "stores()" after some delay, your data will be there. That is why you basically need promises("then" feature). Function that is passed as a parameter of "then(param)" is executed when response is ready.
Working sample:
You can start playing with this sample, and implement what you need:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://www.w3schools.com/angular/customers.php"});
}
};
var Component = {
controller: function() {
var records = Model.getAll();
return {
records: records
}
},
view: function(ctrl) {
return m("div", [
ctrl.records().records.map(function(record) {
return m("div", record.Name);
})
]);
}
};
m.mount(document.body, Component);
If you have more questions, feel free to ask here.
I have functions like the getData function below.
I understand that $http returns a promise. In my current set up I am using $q so that I can do some processing of the results and then return another promise:
var getData = function (controller) {
var defer = $q.defer();
$http.get('/api/' + controller + '/GetData')
.success(function (data) {
var dataPlus = [{ id: 0, name: '*' }].concat(data);
defer.resolve({
data: data,
dataPlus: dataPlus
});
})
.error(function (error) {
defer.reject({
data: error
});
});
return defer.promise;
}
Is there any way that I can do this without needing to use the AngularJS $q (or any other $q implementation) or is the code above the only way to do this? Note that I am not looking for a solution where I pass in an onSuccess and an onError to the getData as parameters.
Thanks
As you say $http.get already returns a promise. One of the best things about promises is that they compose nicely. Adding more success, then, or done simply runs them sequentially.
var getData = function (controller) {
return $http.get('/api/' + controller + '/GetData')
.success(function (data) {
var dataPlus = [{ id: 0, name: '*' }].concat(data);
return {
data: data,
dataPlus: dataPlus
};
})
.error(function (error) {
return {
data: error
};
});
}
This means that using getData(controller).then(function (obj) { console.log(obj) });, will print the object returned by your success handler.
If you want you can keep composing it, adding more functionality. Lets say you want to always log results and errors.
var loggingGetData = getData(controller).then(function (obj) {
console.log(obj);
return obj;
}, function (err) {
console.log(err);
return err;
});
You can then use your logging getData like so:
loggingGetData(controller).then(function (obj) {
var data = obj.data;
var dataPlus = obj.dataPlus;
// do stuff with the results from the http request
});
If the $http request resolves, the result will first go through your initial success handler, and then through the logging one, finally ending up in the final function here.
If it does not resolve, it will go through the initial error handler to the error handler defined by loggingGetData and print to console. You could keep adding promises this way and build really advanced stuff.
You can try:
Using an interceptor which provides the response method. However I don't like it, as it moves the code handling the response to another place, making it harder to understand and debug the code.
Using $q would be the best in that case IMO.
Another (better ?) option is locally augmented transformResponse transformer for the $http.get() call, and just return the $http promise.
I am trying to use $resource to get data from a static json file and here is the code snippet :
angular.module('app.services', ['ngResource']).
factory('profilelist',function($resource){
return $resource('services/profiles/profilelist.json',{},{
query:{method:'GET'}
});
});
In the controller,
function ProfileCtrl($scope,profilelist) {
$scope.items = [];
$scope.profileslist = profilelist.query();
for (var i=0;i<=$scope.profileslist.length;i++){
if($scope.profileslist[i] && $scope.profileslist[i].profileid){
var temp_profile = $scope.profileslist[i].profileid;
}
$scope.items.push(temp_profile);
}
But now, I am facing an error :
TypeError: Object #<Resource> has no method 'push'
Could you please help me where I am going wrong ?
You don't need to specify actions parameter for default $resource methods (these are 'get', 'save', 'query', 'remove', 'delete'). In this case you can use .query() method as is (this requires only service definition to be chaged):
angular.module('app.services', ['ngResource']).
factory('profilelist',function($resource){
return $resource('services/profiles/profilelist.json');
});
P.S. And one more hint is that your example unwrapped json into hash rather then array (that's why you received no push method error), if you need it to be an array set isArray: true to action config:
'query': {method:'GET', isArray:true}
And as #finishingmove spotted you really can't assign $resource result to obtain immediately, provide callback:
$scope.profileslist = profilelist.query(function (response) {
angular.forEach(response, function (item) {
if (item.profileid) {
$scope.items.push(item.profileid);
}
});
});