Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
In angular when using resources we can bind them directly on the $scope like this:
$scope.users = Users.$query();
This could also written as:
Users.$query().$promise.then(function(users) {
$scope.users = users;
});
Have you experienced any downside of using the first approach? What are the pros and cons of each?
From angular ng-book:
$resource Instances Are Asynchronous
With all these methods, it’s important to note that when they are
invoked, the $resource object immediately returns an empty reference
to the data. This data is an empty reference, not the actual data, as
all these methods are executed asynchronously. Therefore, a call to
get an instance might look synchronous, but is actually not. In fact,
it’s simply a reference to data that Angular will fill in
automatically when it arrives back from the server.
// $scope.users will be empty
$scope.users = User.query();
We can wait for the data to come back as expected using the callback method that the methods
provide:
$scope.users(function(users) {
// logic here
});
or use raw $http from $promise attribute
$scope.users.$promise.then(function(users) {
// logic here
});
Both approaches are essentially equivalent.
The main difference between them is that in the 2nd approach, you will be able to perform certain actions once the request completes, whereas in the 1st approach, to be able to run logic when the request completes, you'll need to work with $watch statements on the users variable.
The 1st approach however, will allow you to place default values inside user which could be convenient when binding view before the request completes.
By the way, there is also a 3rd option:
$scope.users = Users.$query();
$scope.users.$promise.then(function(users) {
// perform some logic
});
This allows you to immediately bind views to the users variable in the scope, and at the same time, perform any additional logic you might need once the request completes.
Related
This question already has answers here:
Why use getters and setters/accessors?
(37 answers)
Closed 3 years ago.
I'm learning how to store API call response data in an AngularJS service. In all the examples that I saw, people used functions in the service to get or set values.
app.factory('dataFactory', function() {
let dataFactory = {};
let info;
dataFactory.setInfo = function(value){
info = value;
}
dataFactory.getInfo = function(){
return info;
}
return dataFactory;
});
But I realized that I could get and set values of variables in a service without the use of any functions.
app.factory('dataFactory', function() {
let dataFactory = {};
let dataFactory.info;
});
// Now I can get or set the value of this in my controller
app.controller('myCtrl', [dataFactory, function(dataFactory) {
dataFactory.info = "Value"; // setting the value
let test = dataFactory.info; // getting the value
}])
I would like to know if my approach could potentially lead to any problems. Is it considered a bad practice and if so why?
Preference to data accessors (getters and setters) over exposing a property directly is neither specific to AngularJS nor to JavaScript. Is generally a common practice in the object oriented programming.
One of the main reasons to prefer getters and setters over the direct access to a property is data encapsulation. When the data is defined as a local variable in the lexical environment of the service function (let info;) it is not possible to access it from outside (for example from controller).
Data accessors also give you a flexibility to add data access logic. For example you may want to implement some checks when the getter is called and make a decisions whether to return the data or not. Likewise, in the setter you may verify that the data (the setter was called with) meets the requirements and throw an error if it's not.
Further you may want to check this answer to see many other benefits getters and setters may provide.
I'm relatively new to AngularJS and the problem I'm facing is one of those "I want to inject a Service into an app.config" type of scenarios, which I realise cannot be done. (I'm comfortable with the different between Service and Provider, and why a Service cannot be injected into a .config.)
What I am trying to accomplish is to use angular-schema-form together with angular-translate such that field titles in generated forms are translated.
There is an issue where someone asks how to do this, and the advice given is to take advantage of angular-schema-form's postProcess, which is a property of the Provider. This callback gives you the form object before it is rendered, giving you the opportunity to manipulate it with user code. Therefore translation could be done within here.
The postProcess method is called on the Provider, so it is done within an app.config:
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// within here I can inspect the form object, find all
// properties whose key is "title", and then perform
// language translation on their values.
So, that is apparently the place where I have an opportunity to manipulate control titles and so on.
Over to the angular-translate library, for me to 'manually' translate strings, I can use the $translate service. This provides both synchronous and asynchronous methods to translate a given key string. The synchronous one is $translate.instant(key).
To glue these two together, what I have tried so far (which does work) is to create a 'bridge' method like this:
var app = angular.module('myApplicationName', ['schemaForm', 'pascalprecht.translate']);
....
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// ... code here which iterates over properties
// and finds all control titles ...
key = app.myTranslate(key);
// ....
}
....
});
app.myTranslate = function (key) {
var service = angular.injector(['ng', 'myApplicationName']).get("$translate");
return service.instant(key);
}
This does work, but it seems ugly and unsafe (as presumably there's no guarantee $translate is ready when the callback is first invoked) and the calls to angular.injector(['ng', 'myApplicationName']).get... are presumably expensive.
Is there a better way, or is this the only way I'm going to get it done, considering the constraints of the libraries I'm working with?
I have also considered an alternative approach altogether, which would be to instead perform the translations on the schema or form objects before they are processed by angular-schema-form. This could be done from within Controllers, eliminating the problem of accessing the $translate service. I may end up going down that route, but it would still be nice to understand the best solution for the above scenario.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
As an angularJS newbie, I am puzzled by the fact I have to bind to a function returned from the service to update the view, not the data itself, I couldn't find any official document explaining this, Does anyone know why?
JSFiddle Code Sample
<div ng-controller="MyCtrl">
binding to a function works!
<p ng-bind-html-unsafe="tempLog.returnBuffer()"></p>
<br><br>
bind to the service variable: Doesn't work, why?
<p>log={{tempLog.buffer}}</p>
<br><br>
bind to scope var instead of service var, still doesn't work
<p>log={{logBuffer}}</p>
bind to a scope var which points to the service Function, works!
<p>log={{pLogFunc()}}</p>
<button ng-click="addText('more')">Trace</button><br>
</div>
JS code
var myApp = angular.module('myApp',[]);
myApp.factory('myLog', function() {
var internalBuffer = "";
return {
buffer:internalBuffer,
trace:function(input){
internalBuffer = internalBuffer + "<br>" +input;
buff = input;
},
returnBuffer:function(){
return internalBuffer;
}
}
});
function MyCtrl($scope, myLog){
$scope.tempLog = myLog;
$scope.logBuffer = myLog.buffer;
$scope.pLogFunc = myLog.returnBuffer;
myLog.trace("aaa");
$scope.addText = function(str){
myLog.trace(str)
}
}
This is not an AngularJS binding problem, this is just how javascript works.
In your service:
1 buffer is assigned to a primitive variable internalBuffer.
2 trace() accepts a parameter that changes the internalBuffer primitive
3 returnBuffer() returns the internalBuffer primitive
Since trace() changes the internalBuffer primitive, any binding to buffer does not affect the changes in the internalBuffer, furthermore, returnBuffer() returns the value of the internalBuffer so naturally the changes you made with the trace() function affects the return value of the returnBuffer() function.
Any of these suggestions may work on your end:
[1] If you want to bind from the buffer property of your myLog service, then change your trace() function to something like this:
trace:function(input){
this.buffer = this.buffer + "<br>" +input;
}
[2] You may disregard the buffer property and solely use the returnBuffer() if youdon't want to expose yourinternalBufferand only use thetrace()to have access in changing theinternalBuffer`
[3] You can use both, the buffer property provides access to another buffer format while the internalBuffer holds all the private buffers / format / or anything else that you may not want to expose to the users of the service. Just be sure to update the buffer in your trace() function by using this as well.
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'm wondering if this is possible, but I have a series of functions where promises would be a perfect solution for ordering and whatnot, however these functions do not depend on the data from previous promises. I would like to use promises to control execution order but I still need the functions to have access to the scope closure.
Here is some context as to why. On the client I have some object. Various actions cause the client to save/update this object. We recently had a race condition where two actions were done so close together the server actually corrupted the object in the database. I thought it would work well to use a promise chain so update requests will wait until there are no other pending update requests. The update information I need to send to the server obviously exists in the scope so I need to be able to access that when pendingRequestPromise resolves.
I essentially have something that looks like this:
scope.$on("UPDATE", function(event, callback){
$http.post("update", scope.myObj).success(function(updateInfo){
callback(updateInfo);
});
};
If the user does the right actions fast enough, I send 2 objects to the server which caused some problems last week.
I would suggest using the $q service somewhat like this:
//disable all stuff you want to user not to click with ng-disable
$scope.pageLoading=true;
var update1Promise = $http.post("update1", scope.myObj).success(function(updateInfo){
callback(updateInfo);
}),
update2Promise = $http.post("update2", scope.myObj).success(function(updateInfo){
callback(updateInfo);
}),
update3Promise = $http.post("update3", scope.myObj).success(function(updateInfo){
callback(updateInfo);
});
$q.all([update1Promise,update2Promise,update3Promise])then(function() {
$scope.pageLoading=false;
});