In my meanjs app created using meanjs generator and auto generated CRUD-module, for income below, I want to customize the promise returned by the get method so that I can call methods and do transformations on the retrieved data. So i attempt to change it in my controller:
// Find existing Income
$scope.findOne = function() {
Incomes.get({
incomeId: $stateParams.incomeId
}).then(function(income){
$scope.income = income;
$scope.incomeDollar= income*$scope.dollarRate;
});
This only gives me the error : undefined is not a function, and pointing on then.
What am i doing wrong here? How can I do transformations on the data retrieved from the get method above?
The pattern i am trying to use is in the end of the article here. I want to switch from the short version to the long so that i can do more stuff in my callback.
// LONG:
myModule.controller('HelloCtrl', function($scope, HelloWorld) {
HelloWorld.getMessages().then(function(messages) {
$scope.messages = messages;
});
});
To simplify this, the writer of the article place the promise returned by ‘getMessages’ on the scope:
// SHORTER:
myModule.controller('HelloCtrl', function($scope, HelloWorld) {
$scope.messages = HelloWorld.getMessages();
});
The promise object can be accessed using $promise. From the doc
The Resource instances and collection have these additional
properties:
$promise: the promise of the original server interaction that created
this instance or collection.
On success, the promise is resolved with the same resource instance or
collection object, updated with data from server...
So, your code should look like:
// Find existing Income
$scope.findOne = function() {
Incomes.get({
incomeId: $stateParams.incomeId
}).$promise.then(function(income){
$scope.income = income;
$scope.incomeDollar= income*$scope.dollarRate;
});
Related
I want to save JSON-Data from my Rest-API in a $scope variable for further usage.
The problem is when the Code is executed, i see in Firebug that i've got the JSON Data sucessfully, but the problem is, that i cant save it in my Scope variable and i dont know why.
My app.js
var app = angular.module('shop', ['ngRoute','ngResource'])
.factory('Customerservice', function ($resource) {
return $resource('http://localhost:8080/Shop/:customer',{customer: "#customer"});
})
.config(function ($routeProvider, $locationProvider) {
$routeProvider.when('/name/:ID', {
templateUrl : "Customer/customers.html",
controller : 'customerController'
});
})
.controller('customerController', function ($scope,Customerservice) {
$scope.customerinfo = Customerservice.get({customer: "Mark"});
alert($scope.customerinfo);
});
Like i said, i've got the JSON-Data but the Problem is in my Controller "customerController". I just put the alert function in my Code to see whats in my $scope.customerinfo. And well the Content of customerinfo is just object: Object.
I noticed something strange while debbuging with Firebug. It looks like that the alert is executed before the get request. This would explain why there is no data in my $scope variable. Can anyone help me here.
Use $promise property
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.
$scope.customerinfo = CustomerService.get({customer: "Mark"});
console.log($scope.customerinfo); //Empty object
However, the $resource service also attaches a $promise property that can be used to delay code execution until the data has arrived from the server:
$scope.customerinfo = CustomerService.get({customer: "Mark"});
console.log($scope.customerinfo); //Empty object
//USE $promise
$scope.customerinfo.$promise
.then(function(info) {
console.log($scope.customerinfo); //Fulfilled object
return info;
}).catch(function(errorResponse) {
console.log(errorResponse.status);
throw errorResponse;
});
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
On success, the promise is resolved with the same resource instance or collection object, updated with data from server. This makes it easy to use in resolve section of $routeProvider.when() to defer view rendering until the resource(s) are loaded.
On failure, the promise is rejected with the http response object, without the resource property.
— AngularJS ngResource $resource API Reference
$resource is async api so you can't get value from direct return of function call, its contains a variable $promise which will return promise so you need to call then function of it
Try this
UserService.get({customer: "Mark"}).$promise.then(function(data) {
$scope.customerinfo = data;
alert($scope.customerinfo);
});
I have a method in my angular 1.5 controller, as shown below but I wanted to refactor the ajax call into the factory itself but I'm having problems with promises.. I'm trying to get to a point where in my controller I can just call the method like I've shown below. Is this possible? I'm trying to avoid having the ...success(function(...) in the controller code.
Any help much appreciated.
Trying to move to
vm.member = someFactory.getMember(vm.id);
Existing working controller code
vm.myMethod = myMethod;
...
function myMethod() {
someFactory.getMember(vm.id).success(function(response) {
vm.member = response;
});
}
When I move the getMethod line into the factory the response is populated obviously but as soon as I come back to the controller, even with the return value from the factory being the response the result is undefined. I know this is because of promises but is there a design pattern I'm missing or a clean way of doing this. Using my currently approach my controller is littered with .success(function()...)
Many thanks!
The procedure is called promise unwrapping.
Besides the fact that success is deprecated and should be replaced with then,
someFactory.getMember(vm.id).then(function(response) {
var data = res.data;
...
});
it is totally ok to have this in controller.
The alternative to this pattern is to return self-filling object (something that ngResource $resource does):
function getMember(...) {
var data = {};
$http(...).then(function (response) {
// considering that response data is JSON object,
// it can replace existing data object
angular.copy(data, response.data);
});
return data;
}
In this case controller can get a reference to the object instantly, and the bindings of object properties {{ vm.member.someProperty }} will be updated in view on response.
The pattern is limited to objects (and arrays), scalar values should be wrapped with objects.
Let's say I have the following...
myService = Restangular.all('things');
myService.getList().then(
// success
function(things) {
$scope.things = things;
},
// failure
function(things) {
// do whatever, stuff failed
}
)
Now I have $scope.things which is a collection of things from the api, all well and good.
I want to post a new thing, and return the promise so I can deal with the pass/fail elsewhere
return $scope.things.post(newThing) // A promise...
However, doing things this way DOESN'T automatically add my new thing to the $scope.things collection. Why not? I've seen questions that link to the enhanced promises section of restangular docs and mention the "push" method, but that doesn't help me because $scope.things has no "push" method.
What's going on here? Where am I getting confused.
As mentionned in Restangular docs:
Because Restangular returns promises, we can then call methods on the returned data on promises so that we can run a function after the promise has completed. For instance, after we update a collection, we can then refresh the collection on our scope:
messages.post(newMessage).then(function(newMsg) {
$scope.messages = messages.getList();
}, function error(reason) {
// An error has occurred
});
I am trying to call a service in angular.js through a controller on load and return a promise. I then expect the promise to be fulfilled and for the DOM to be updated. This is not what happens. To be clear, I am not getting an error. The code is as follows.
app.controller('TutorialController', function ($scope, tutorialService) {
init();
function init() {
$scope.tutorials = tutorialService.getTutorials();
}
});
<div data-ng-repeat="tutorial in tutorials | orderBy:'title'">
<div>{{tutorial.tutorialId}}+' - '+{{tutorial.title + ' - ' + tutorial.description}}</div>
</div>
var url = "http://localhost:8080/tutorial-service/tutorials";
app.service('tutorialService', function ($http, $q) {
this.getTutorials = function () {
var list;
var deffered = $q.defer();
$http({
url:url,
method:'GET'
})
.then(function(data){
list = data.data;
deffered.resolve(list);
console.log(list[0]);
console.log(list[1]);
console.log(list[2]);
});
return deffered.promise;
};
});
Inside of the ".then()" function in the service, I log the results and I am getting what I expected there, it just never updates the DOM. Any and all help would be appreciated.
getTutorials returns promise by itself. So you have to do then() again.
tutorialService.getTutorials().then(function(data){
$scope.tutorials = data;
});
Before that, $http returns a promise with success() and error().
Although you can also use then as well
Since the returned value of calling the $http function is a promise,
you can also use the then method to register callbacks, and these
callbacks will receive a single argument – an object representing the
response.
So you are correct with that.
What is your data coming from the http call look like? Your code works - I created a version of it here http://jsfiddle.net/Cq5sm/ using $timeout.
So if your list looks like:
[{ tutorialId: '1',
title : 'the title',
description: 'the description'
}]
it should work
In newer Angular versions (I think v 1.2 RC3+) you have to configure angular to get the unwrap feature working (DEMO):
var app = angular.module('myapp', []).config(function ($parseProvider) {
$parseProvider.unwrapPromises(true);
});
This allows you to directly assign the promise to the ng-repeat collection.
$scope.tutorials = tutorialService.getTutorials();
Beside that I personally prefer to do the wrapping manually:
tutorialService.getTutorials().then(function(tutorials){
$scope.tutorials = tutorials;
});
I don't know the exact reason why they removed that feature from the default config but it looks like the angular developers prefer the second option too.
I have a factory called "Server" which contains my methods for interaction with the server (get/put/post/delete..). I managed to login and get all data successfully when I had all my code in my controller. Now that I want to separate this code and restructure it a little bit I ran into problems. I can still login and I also get data - but data is just printed; I'm not sure how to access the data in controller? I saw some ".then" instead of ".success" used here and there across the web, but I don't know how exactly.
This is my factory: (included in services.js)
app.factory('Server', ['$http', function($http) {
return {
// this works as it should, login works correctly
login: function(email,pass) {
return $http.get('mywebapiurl/server.php?email='+email+'&password='+pass').success(function(data) {
console.log("\nLOGIN RESPONSE: "+JSON.stringify(data));
if(data.Status !== "OK")
// login fail
console.log("Login FAIL...");
else
// success
console.log("Login OK...");
});
},
// intentional blank data parameter below (server configured this way for testing purposes)
getAllData: function() {
return $http.get('mywebapiurl/server.php?data=').success(function(data) {
console.log("\nDATA FROM SERVER: \n"+data); // here correct data in JSON string format are printed
});
},
};
}]);
This is my controller:
app.controller("MainController", ['$scope', 'Server', function($scope, Server){
Server.login(); // this logins correctly
$scope.data = Server.getAllData(); // here I want to get data returned by the server, now I get http object with all the methods etc etc.
…. continues …
How do I get data that was retrieved with $http within a factory to be accessible in controller? I only have one controller.
Thanks for any help, I'm sure there must be an easy way of doing this. Or am I perhaps taking a wrong way working this out?
EDIT: I also need to be able to call factory functions from views with ng-click for instance. Now I can do this like this:
// this is a method in controller
$scope.updateContacts = function(){
$http.get('mywebapiURL/server.php?mycontacts=').success(function(data) {
$scope.contacts = data;
});
};
and make a call in a view with ng-click="updateContacts()". See how $scope.contacts gets new data in the above function. How am I supposed to do this with .then method?(assigning returned data to variable)
My question asked straight-forwardly:
Lets say I need parts of controller code separated from it (so it doesn't get all messy), like some functions that are available throughout all $scope. What is the best way to accomplish this in AngularJS? Maybe it's not services as I thought …
The trick is to use a promise in your service to proxy the results.
The $http service returns a promise that you can resolve using then with a list or success and error to handle those conditions respectively.
This block of code shows handling the result of the call:
var deferred = $q.defer();
$http.get(productsEndpoint).success(function(result) {
deferred.resolve(result);
}).error(function(result) { deferred.reject(result); });
return deferred.promise;
The code uses the Angular $q service to create a promise. When the $http call is resolved then the promise is used to return information to your controller. The controller handles it like this:
app.controller("myController", ["$scope", "myService", function($scope, myService) {
$scope.data = { status: "Not Loaded." };
myService.getData().then(function(data) { $scope.data = data; });
}]);
(Another function can be passed to then if you want to explicitly handle the rejection).
That closes the loop: a service that uses a promise to return the data, and a controller that calls the service and chains the promise for the result. I have a full fiddle online here: http://jsfiddle.net/HhFwL/
You can change the end point, right now it just points to a generic OData end point to fetch some products data.
More on $http: http://docs.angularjs.org/api/ng.%24http
More on $q: http://docs.angularjs.org/api/ng.%24q
$http.get retuns a HttpPromise Object
Server.getAllData().then(function(results){
$scope.data = results;
})