How to use 'Controller as' with $http in Angular - angularjs

I'm trying to load some data through $http to prefill a profile form. Unfortunately all the examples I find online use the $scope-approach rather than 'Controller as'-approach (which I'm using to make future transition to Angular 2 easier). These examples assign the $http response to the '$scope' variable, which is not possible when using 'this'.
After a lot of fiddling I managed to get it to work by adding a temp variable
var temp = this;
to which I can assign the $http response when it successfully returns.
angular.module('angularUserApp')
.factory('ajax',['$http',function($http){
return {
getProfile: function(){
return $http.get('/ajax/user/profile')
.then(function(response){
return response.data.data.profile;
});
}
}
}])
.controller('userProfileCtrl', ['ajax', function (ajax) {
var temp = this;
ajax.getProfile().then(function(response){
temp.profile = response;
});
}]);
Is there a more elegant approach?

Your approach for using controllerAs is correct. Just a few advices though, better alias the this to a variable vm, which stands for viewModel instead of temp, and name your service semantically: userService instead of ajax:
angular.module('angularUserApp')
.factory('userService', function ($http){
return {
getProfile: function () {
return $http.get('/ajax/user/profile')
.then(function (response){
return response.data.profile;
});
}
}
})
.controller('userProfileCtrl', function (userService) {
var vm = this;
userService.getProfile().then(function (response) {
vm.profile = response;
});
});

One of the design ideas of angular is that you can use $scope. Simply inject $scope into your controller, like you did with your ajax service and set the profile response to the $scope variable ($scope.profile = response;).
As a result you would get something like:
angular.module('angularUserApp')
.factory('ajax',['$http',function($http){
return {
getProfile: function(){
return $http.get('/ajax/user/profile')
.then(function(response){
return response.data.data.profile;
});
}
}
}])
.controller('userProfileCtrl', ['ajax', '$scope', function (ajax, $scope) {
ajax.getProfile().then(function(response){
$scope.profile = response;
});
}]);
// In your template
<div ng-controller="userProfileCtrl">
<div ng-bind="profile.name"></div>
</div>

Related

How broadcast data of HTTP post method in angularjs

I'm using the same HTTP method in different controller like following sample:
Service:
var method="sampleMethod"
HotalStatisticService.GetReservations = function (data) {
return $http({
method: 'POST',
data: data,
cache: false,
url:'http://sample.com/'+method
});
}
first controller
.controller("SampleACtrl", function ($scope,HotalStatisticService ) {
HotalStatisticService.GetReservations({start:start, end:end, type:type})
.success(function (data) {
$scope.sampleA=data;
})
}
second controller
.controller("SampleBCtrl", function ($scope,HotalStatisticService ) {
HotalStatisticService.GetReservations({start:start, end:end, type:type})
.success(function (data) {
$scope.sampleB=data;
})
}
How do I use here method in the only one controller?
Let me say that the other solution that uses factories is probably a MUCH better solution.Using Services is also a good option.
Another ,perhaps crude way to do it is using $rootScope.Answer below.
What you essentially want to do is share data between the two controllers. Based on your reply from the comments, both the controllers belong to the same module.You can use $rootScope here to act as a common point.
As you can see, i've added $rootScope as a dependency in both the controllers and simply printed the txt variable in the second div.
JS Code
var app = angular.module('plunker', []);
app.controller('ACtrl', function($scope,$rootScope) {
$scope.name = 'This is Controller A ';
$scope.execute = function() {
alert('Executed!');
}
$rootScope.txt="Hi there from the other side";
});
app.controller('BCtrl', function($scope,$rootScope) {
$scope.name = 'This is Controller B ';
});
HTML
<div ng-controller="ACtrl">
<p>Hello {{name}}!</p>
</div>
<div ng-controller="BCtrl">
<p>Hello {{name}}!</p>
{{txt}}
</div>
Here is the DEMO
What I'd do is create a factory service that handles your HTTP requests, and then inject that service into your controllers... Code would look something like this:
var app = angular.module('sampleApp');
app.factory('sampleFactory', ['$http',
function($http) {
return {
getData: function(success, fail) {
if (_dataStore) {
success(_dataStore);
}
$http({
//fill in your params here
})
.then(
function successCallback(response) {
//store data in the _dataStore object
success(response)
}, fail(response))
},
_dataStore: {}
}
}
]);
app.controller('SampleACtrl', ['$scope', 'sampleFactory',
function($scope, sampleFactory) {
$scope.sampleA = sampleFactory.getData(success, fail);
}
]);
app.controller('SampleBCtrl', ['$scope', 'sampleFactory',
function($scope, sampleFactory) {
$scope.sampleB = sampleFactory.getData(success, fail);
}
]);
The main idea behind doing it like this is that you only make the HTTP request once, and then store the data in the factory as an object. When you call the getData() function on that object, you'll either get what is actually in the factory already (meaning the request was already made) or you'll go make the request. You'd pass in 2 functions (success or fail) as your response functions to the $http call. This is by far not 100% good, there's many improvements(add $q in there to return promises, etc) but it's a start in the right direction.
Long story short: USE FACTORIES!

AngularJs, calling a $resource from within an Interceptor

I have just about given up with this. But I have a $resource that uses a query() to retrieve a list of items. I then have an Interceptor that runs over those items in order to insert an $interval.
The $interval at a specific point will then get an item again using the $resource's get() function. But its this get() that is not working.
The call happens but I cannot get the response back into the template.
myServices.factory('Items', ['$resource',
function($resource) {
return $resource("/items", {}, {
'query': {
interceptor: MyInterceptor,
url: "/items",
isArray: true
},
})
}]);
myServices.factory('MyInterceptor', function($q, $interval, $injector, $rootScope) {
return {
'response': function(response) {
angular.forEach(response.resource, function (item) {
$interval(function () {
item.something = 1 + item.something;
if(item.something == 10)
{
item = $injector.get("Mine").get({slug: item.id});
}
});
});
return response;
}
};
});
I thought that this line item = $injector.get("Mine").get({slug: item.id}); would work, but it doesn't. The new item is not changed in the template.
So I changed it to something like this, which did not work either;
$injector.get("Mine").get({slug: item.id}, function(data){
item = data;
});
I have tried with $q and $promise too, but I had no luck with those either. Finding decent examples on those subjects was tough too.
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
In short ...... I am using an Interceptor inside a $resource, with an $interval which then needs to eventually change a single value within an array of values within the $scope - how can I get this to work?
Based on the above statement I will give an answer.
First things first, remove the interceptor. You won't need it. Instead use a service.
Write a service called ProcessedItems.
angular.module('app')
.service('ProcessedItems', ['Items', '$q', function(Items, $q){
return {
query: function() {
var defer = $q.defer();
Items.query()
.$promise
.then(function(response){
angular.forEach(response.resource, function(i)){
i.s = 1 + i.s;
if(i.s == 10) {
i = $injector.get("Mine").get({slug: i.id});
i.$promise.then(function(){
defer.resolve(response);
}, function(){
defer.reject();
});
};
};
});
return defer.promise;
}
};
}]);
After this service is set up, in your controller you can do
angular.module('app')
.controller('AppController', ['$scope', 'ProcessedItems',
function($scope, ProcessedItems){
$scope.items = [];
ProcessedItems.query().then(function(pitems){
$scope.items = pitems;
});
});
What this will essentially do is first process the data completely and then display it in the view.

Making the most of my factory

I am new to Angular, so if you ask the question: "Why don't you...?" The answer is...because I didn't know I could.
Have a factory make an API call, then inject that factory into a parent controller that will have scope over the entire page. Then have child controllers nested and inherit from the parent controller.
Here is what I have so far. I may be WAY off here, and if that is the case, please tell me. I am working on this alone, and have no help, so any help is welcomed.
Thank you.
var app = angular.module('myApp', []);
app.factory('myFactory', function($http){
var MyFactory = function(){};
MyFactory.getParams = function(){
return $http.get('/getparameters');
.success(function(data){
var roomname = data.roomname;
})
MyFactory.getRoom(roomname);
};
MyFactory.getRoom = function(room){
return $http.get('/my/api/' + room);
};
});
app.controller('RoomCtrl', function($scope, myFactory){
$scope.data = myFactory;
});
You don't need to use resource, you need use promise;
EDITED
I tried to make more clear for you
app.factory('apicall', ['$q','$http',function($q, $http){
function getRoom(options){
var promise = $http.get(options.paramUrl)
.success(function(data){
//handle your error
return $http.get(options.roomUrl + data.roomname);
})
.error(function(msg){
return $q.when(msg)
})
return promise;
}
return {getRoom:getRoom};
}])
if you want call your factory in where you want
app.controller('RoomCtrl', ['$scope','apicall',function($scope, apicall){
var options = {
paramUrl:'/getparameters',
roomUrl:'/my/api/'
}
apicall.getRoom.all(function(result){
$scope.data = result;
})
}]);
The $q service helps you to handle combination of two asynchronous calls
app.factory('apicall', function($q, $http) {
var deferred = $q.defer();
$http.get('/getparameters')
.success(
function(data) {
var roomname = data.roomname;
$http.get(baseURL + 'room/' + roomname)
.success(
function(roomData) {
deferred.resolve(roomData);
}
)
.error(
function() {
deferred.reject();
}
)
})
.error(
function() {
deferred.reject();
}
);
return deferred.promise;
});
And here is controller where you can use your service
app.controller('someCtrl', function(apicall) {
apicall.then(
function(roomData) {
//success
},
function() {
//error
}
)
})
I usually separate different API calls in different factories that return a $resource.
For example, let's say we have 2 different API calls that point to different resources:
yourwebsite.com/user/:userId - returns data about the user
yourwebsite.com/photo/:photoId - return data about some photo
In angular, you would split these in 2 different factories:
"use strict";
angular.module("App.services").
factory("User",["$resource", function($resource){
return $resource("yourwebsite.com/user/:userId"),{userId:"#userId"});
}]);
and second
angular.module("App.services").
factory("Photo",["$resource", function($resource){
return $resource("yourwebsite.com/photo/:photoId"),{photoId:"#photoId"});
}]);
In your controller, you would use them like so:
angular.module("App.controllers").controller("TestController",["User","Photo", function(User, Photo){
User.get({
id:10
}).$promise.then(function(data){
console.log(data); //returns the data for user with the id=10
});
Photo.get({
id:123
}).$promise.then(function(data){
console.log(data);
});
}]);
Usually $resource maps a CRUD API(what I posted above is a basic example of a GET call).Check out the documentation on $resource - It already has the basic GET, PUT, POST, DELETE functions.
I would recommend using $http if you have only 1 simple operation on that URL and not all 4 of them. You can then inject $http in your controller and do the request there instead of creating a factory for it.
If you have 2 requests and they are chained(second one depends on the data received from the first one) you have to wait until the first one is resolved. With $resource you can do this in the following way:
angular.module("App.controllers").controller("TestController",["User","Photo", function(User, Photo){
var user_promise = User.get({
id:10
}).$promise.then(function(data){
console.log(data); //returns the data for user with the id=10
return data;
});
var photo_promise = Photo.get({
id:123
}).$promise.then(function(data){
console.log(data);
return data;
});
user_promise.then(photo_promise).
catch(function(error){
console.log(error); //catch all errors from the whole promise chain
});
}]);

update a service variable within an $http callback

I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.
I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.
Here is what I have thus far.
app.service('UserService', ['$http', function($http) {
this.users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(this.users);
}
};
promise.then(function() {
// this.users is undefined here
console.log('this.users');
});
}]);
Any help is greatly appreciated. Thank you.
Try using
var users = [];
rather than
this.users = [];
and see what
console.log(users);
outputs in each of those cases.
Your service is oddly defined, but if you have a return in it you can access it from any controller:
app.service('UserService', ['$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(users);
users = data.data;
}
};
return {
getUsers: function(){
return users;
}
}
}]);
so in your controller, you can use:
var myUsers = UserService.getUsers();
UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave
// your service should return a promise
app.service('PickerService', [$http', function($http) {
return {
getFiles: function(){
return $http.get('files.json'); // this returns a promise, the promise is not executed here
}
}
}]);
then in your controller do this:
PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
$scope.myDirectiveData = returnValues.data;
});
this does not have scope anymore where you are trying to use it do this instead:
app.service('UserService', [$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
console.log(users);
}
};
promise.then(function() {
console.log(users);
});
}]);
all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.
I think what your asking for is a solution along the lines of defining your service like this:
angular.module('app')
.service('User', function($http, $q) {
var users = null;
var deferred = $q.defer()
return {
getUsers: function() {
if(users) {
deferred.resolve(users);
} else {
$http.get('users.json');
.success(function(result) {
deferred.resolve(result);
})
.error(function(error) {
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Then in one Each controller you would have to do this:
angular.module('app')
.controller('ACtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in BCtrl
$scope.users = users;
});
});
angular.module('app')
.controller('BCtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in ACtrl
$scope.users = users;
});
});
NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.
All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations

How can I get a service to access server data via an $http call?

I make an $http call inside a service that is supposed to get data from my server. For some reason I can't get my service to work - nothing happens. I know the server code works because if I place the $http call inside a function within my controller, then it gets the server data as expected. Here is the code I have so far:
app.service('myService', function($q,$compile,$http) {
this.getData = function() {
var deferred = $q.defer();
var promise = $http.get('myfile.php').success(function (data) {
var response = $compile(data)($scope);
deferred.resolve(response);
});
return deferred.promise;
};
});
Also, I know the code that uses this service works because if I do something like the following,
app.service('myService', function() {
this.getData = function() {
return 'TEST';
};
});
then I will see the word "TEST" show up in the div that utilizes this service. I feel like I'm very close, but there is something I am missing. Any ideas on what I'm doing wrong?
UPDATE:
controller: function($scope, $http, $rootScope, myService){
var scope = $rootScope;
scope.viewMyData = function() {
var element = myService.getData();
$('#myDiv').html(element);
}
}
HTML:
<div ng-click="viewMyData()">Click Here</div>
<div id="myDiv"></div>
If I strip the code in myService and simply return TEST (as above), then I will see "TEST" show up in id="myDiv". But for some reason the $http call isn't being triggered.
#tymeJV is right, but here's my attempt to spell out the example better. $http returns a promise interface that allows you to chain callbacks to be executed when the $http response returns. So, in this case, calling myService.getData() can't return the result immediately (it's off getting the data from the server), so you need to give it a function to execute when the server finally responds. So, with promises, you simply attach your callback using the thePromise.then(myCallbackFunc).
Service
app.service('myService', function($q,$compile,$http) {
this.getData = function() {
var promise = $http.get('myfile.php');
promise = promise.then(function (response) {
return response.data;
});
return promise;
};
});
Controller
controller: function($scope, $rootScope, myService){
var scope = $rootScope;
scope.viewMyData = function() {
myService.getData().then(function(data) {
$('#myDiv').html(element);
});
}
}
Use .then in the controller to continue the promise pattern:
myService.getData().then(function(data) {
$('#myDiv').html(data);
});

Resources