Trying to get my head around this. I have a simple factory using ngResouce, like this:
.factory('FooResource', function($resource) {
var foo = $resource('/api/foo').get();
return foo;
})
And in my app, in multiple places, in multiple controllers over time, I use the value of 'FooResource.bar' (where 'bar' is returned in the data from the get() call).
Is it true that the network call to '/api/foo' will only happen on the first reference for the life of my SPA? Does that first reference need to 'FooResource.bar' be handled like a promise?
From what I see in my playing around with code, it seems like the first question is 'yes' and the second is 'no', but don't know if that's really true in general, or just happening because its a small test app on my dev box.
Edit: I guess part of what I want validation on is my thinking that since this is in a factory, which is a singelton, the $resource call will only ever be made once. Is that true?
Depends, and yes. You will always need to handle it as a promise, and you can enable/disable the http cache. If you have the cache set to true, then the request will send off once and be cached until the cache is cleared.
You can find more about the $resource caching in the $resource documentation here: https://docs.angularjs.org/api/ngResource/service/$resource
Related
I am attempting to implement a sample application with Angular that interacts with a backend REST API using $resource objects. However, the backend system does not generate id's for the resources, so these need to be defined on the objects being created on the client. This causes a problem when invoking the $save method on the new'ed resource because it forces the JSON data to be POSTed to the wrong URL, i.e., it POSTs to:
/resources/employees/1234
rather than:
/resources/employees
I would prefer not to have to drop down to using the low level $http service if I can avoid it.
Does anyone know how I can work around this issue?
Thanks.
This is because of the fact that you configured your $resource constructor in this way, for example:
$resource('resources/employees/:employeeId', {
employeeId: #id
});
That means that when you call methods like $save or $delete etc. on the resource objects made by this constructor, the variable :employeeId in the url will be filled with the value id that exist on the object on which you called the method. To avoid this you have to modify the constructor config so that the url variable does not depend on the object id property.
Let's say in a service I need to make an http request and store the value, and then if something else uses the same service it could just reuse that stored value, without need of sending a request:
_cachedValue = null
.factory('myService', ($http, $q)->
getFoo: ->
dfrd = $q.defer()
if _cachedValue == null
$http.get('/foo').success (data)->
_cachedValue = data
dfrd.resolve(_cachedValue)
else
dfrd.resolve(_cachedValue)
return dfrd.promise
That kinda works. The problem is - when two consumers simultaneously try to invoke the method, it would send two requests. So I need to send the request only if it never has been sent, and when the second consumer invokes getFoo, it would just wait till the request comes through.
Repeatedly checking every few milliseconds until _cachedValue isn't null - sounds pretty dumb, right? I can't $watch on the value change (there's no $scope inside the service). I can though utilize angular's event bus, and emit an event via $rootScope. But that sounds very overkill, since nobody outside of the service would be interested in listening to that kind of event. Can someone suggest a better way?
You should only set the $http cache property to true.
This will prevent more request if a request is already in progress.
see: https://github.com/angular/angular.js/blob/master/src/ng/http.js#L987
I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!
I apologize if this is a real newbie question: I'm coming to AngularJS and restangular as a cold start - I'm porting some older, back-end oriented code into this fancy new world of SPAs and XHRs.
Anyway, this is really simple: I want to load an object from my API. I'm able to do that reasonably easily this way:
App.factory('User', function($cookies, Restangular) {
if (!$cookies.user_id) {
return {name: 'Nobody', userId: -1};
} else {
return Restangular.one('users', $cookies.user_id).get();
}
});
App.controller('ApplicationCtrl', function ($scope, User) {
$scope.user = User;
});
This will (as I understand it) let me inject the current user wherever I need it (and if the cookies aren't right, they end up getting a 404 or 401 on the API call, depending.
The simple problem I'm having now is that when I want to access this user object in my templates, it's not bound to {{user}}, but rather {{user.user}}, as I injected $scope.user with my User object, which was a JSON-created object that restangular fetched (it looked like {"user":{"id":2,"name":"Eric Eslinger","email":"eric.eslinger#gmail.com"}} so if I want to say, "hello username" I have to say "hello, {{user.user.name}}". Which is unsatisfying.
Any advice about what I'm missing in setting up my object heirarchy is welcome; I have a feeling I'm just missing something simple but also important that I'm going to need to worry about later.
To answer my own question, this isn't really a restangular thing, it's a general angular thing (ngResource behaves the same way) and is a bit of orthoganality to how EmberJS works. In EmberJS, (or at least the version of Ember-Data that I was using), JSON api responses are expected to have a root element, which Ember-Data kind of disregards (or uses to type the result objects). $resource and restangular attend to the root elements. So you can alter your api to just return {"id:2" ... instead of {"user":{"id":2 ... or you can do a transformResponse on the result. Your call.
Since I'm writing both the API and the front end, and there's nobody else using this API right now (once it stabilizes, that will change), I just went and changed how the API output JSON. The backend uses active_model_serializers, so I just put a , root: false in a few choice locations for now. Easy enough.
Using Restangular for AngularJS, keep getting an object object from Mongolab.
I'm sure it has to do with Promise but not sure how to use/implement this coming from old Java OO experience.
(Side note, would something like Eloquent Javascript, some book or resource help me understand the 'new' Javascript style?)
The small web app is for Disabled Students and is to input/edit the students, update the time they spend after school and then output reports for their parents/caregivers every week.
Here's the code that returns undefined when popping up a new form (AngularJS Boostrap UI modal)
I personally think Restangular & the documentation is a great addition so hope it doesn't dissuade others - this is just me not knowing enough.
Thanks in advance
app.js
...
$scope.editStudent = function(id) {
$scope.myStudent = Restangular.one("students", id);
console.log($scope.myStudent);
}
I'm the creator of Restangular :). Maybe I can help you a bit with this.
So, first thing you need to do is to configure the baseUrl for Restangular. For MongoLab you usually do have a base url that's similar to all of them.
Once you got that working, you need to check the format of the response:
If your response is wrapped in another object or envelope, you need to "unwrap" it in your responseExtractor. For that, check out https://github.com/mgonto/restangular#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case
Once you got that OK, you can start doing requests.
All Restangular requests return a Promise. Angular's templates are able to handle Promises and they're able to show the promise result in the HTML. So, if the promise isn't yet solved, it shows nothing and once you get the data from the server, it's shown in the template.
If what you want to do is to edit the object you get and then do a put, in that case, you cannot work with the promise, as you need to change values.
If that's the case, you need to assign the result of the promise to a $scope variable.
For that, you can do:
Restangular.one("students", id).get().then(function(serverStudent) {
$scope.myStudent = serverStudent;
});
This way, once the server returns the student, you'll assign this to the scope variable.
Hope this helps! Otherwise comment me here!
Also check out this example with MongoLab maybe it'll help you :)
http://plnkr.co/edit/d6yDka?p=preview