I'm trying to change some $rootscope variables from within a controller after a I have received a promise from a service.
The $rootscope variables are used to set the html page title attribute etc.
Below is the code I have, I created a function called changeRootPageNotFound() to change the $rootscope variables. It does not work if it's called in the promise.then function.
app.controller('mainController', ['$routeParams', '$scope', '$rootScope', 'mainService', function ($routeParams, $scope, $rootScope, mainService) {
var mainCtrl = this;
mainCtrl.id = $routeParams.itemId;
var promise = mainService.getData($routeParams.id);
promise.then(function (response)
{
if (response.data.data) {
mainCtrl.data = response.data.data;
} else {
mainCtrl.data = false;
changeRootPageNotFound();
}
});
function changeRootPageNotFound() {
$rootScope.title = "Page Not Found - 404";
$rootScope.titleSuffix = "";
}
// changeRootPageNotFound(); // works here
}]);
How can I change the $rootscope variables after I have received the deferred promise from the service?
Add a .catch method:
promise.then(function (response)
{
//if (response.data.data) {
mainCtrl.data = response.data.data;
//} else {
// mainCtrl.data = false;
// changeRootPageNotFound();
//}
}).catch(function(errorResponse) {
console.log(errorResponse.status);
mainCtrl.data = false;
changeRootPageNotFound();
throw errorResponse;
});
The $http service rejects the promise when the status is outside the range 200-299.
What is the throw errorResponse; for, can it be left out?
If the throw errorResponse is omitted, the rejection handler returns a value of undefined. This will convert the rejected promise to a fulfilled promise that resolves as undefined. If there is no further chaining, it can be left out.
A common cause of problems is programmers being unaware of this and unintentionally converting promises.
instead of .catch you can pass the same function to then as the 2nd argument
One of the subtle differences between .catch and using the 2nd argument of the .then method, is that runtime errors in the .then success handler will not be caught in the rejection handler of the 2nd argument.
According to your snippet your code should have worked. In my plunker its working after the deferred promise also.
// Code goes here
angular.module('Test',[])
.service('Service', function($q){
this.ts = function(){
var deferred = $q.defer();
deferred.resolve("hello")
return deferred.promise;
}
})
.controller('Controller', function(Service, $rootScope){
Service.ts().then(function(response){
$rootScope.title="hello";
changeRootPageNotFound();
});
function changeRootPageNotFound() {
$rootScope.title = "Page Not Found - 404";
$rootScope.titleSuffix = "";
}
});
Here is the html
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.5.8" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="Test">
<div ng-controller="Controller">
<h1>{{title}}</h1>
</div>
</body>
</html>
Please check this Plunker https://plnkr.co/edit/THXDYrWuTqR8UYSJlerB?p=preview
Related
I am newbie learning to make back end calls from my angular app's service, I am making the back end call from the angular's Service.
I am calling the function in the service from the controller.
The rest service I provided is not the actual service I am hitting, for some reasons I cannot disclose it. I am sure that the rest service I have is valid and is working, cause I was able to hit it though the controller, which is a bad way of doing it, so this is the reason i want to change the back end call to the service.
Below is my js file. Any help would be appreciated, please feel free to let me know if I am doing this wrong.
angular.module("myApp",[])
.controller("myCont", ['myService', function($http, myService){
var vm = this;
this.myUrl = "some rest service";
console.log("The controller");
vm.getDataInController = function() {
console.log("The function is called");
myService.getData(vm)
.success(function (custs) {
console.log("The data is obtained");
})
.error(function (error) {
console.log("some error occurred");
});
}
}])
.service('myService', ['$http', function ($http) {
this.getData = function (vm) {
console.log("control is in the service");
console.log(vm.myUrl);
$http({
type: 'GET',
url: vm.myUrl
// data is where we have the JSON returned in the form of OBJECT from the gis
}).then(function successCallback(response) {
console.log("the backend call worked");
}), function errorCallback(response) {
console.log("the backend call worked");
}
};
}])
;
My Html file is
<!DOCTYPE html>
<html >
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src = "angular-min.js"></script>
<script src = "sampleOneScript.js"></script>
</head>
<body ng-app = "myApp" ng-controller = "myCont as main">
{{main.myUrl}}
<br>
<button type = "button" ng-click = main.getDataInController()>Click me </button>
</body>
</html>
The error I got in the console.
TypeError: Cannot read property 'getData' of undefined
at vm.getDataInController (http://localhost:63342/exercise.weokspce/ng-repeat%20example/sampleOneScript.js:15:26)
at fn (eval at (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:212:87), :4:275)
at f (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:252:82)
at m.$eval (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:132:366)
at m.$apply (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:133:60)
at HTMLButtonElement. (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:252:134)
at HTMLButtonElement.Hf.c (http://localhost:63342/exercise.weokspce/ng-repeat%20example/angular-min.js:35:137)
The problem may be that you are not injecting properly all the dependencies for "myCont". Try changing the line:
.controller("myCont", ['myService', function($http, myService){
with:
.controller("myCont", ['$http', 'myService', function($http, myService){
and see if that corrects things
I am trying to get hands in promises. SO i wrote a sample code like below
<!doctype html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<script src="../angularjs.js"></script>
</head>
<body>
<div ng-controller="CartController">
</div>
<script>
var app = angular.module('myApp', []);
app.controller('CartController', function($scope, $q,$http){
$scope.newFun = function()
{
var defered = $q.defer();
$http.get('data.json').success(function(data) {
console.log(data);
defered.resolve(data);
})
.error(function(data, status) {
console.error('Repos error', status, data);
});
return defered.promise;
}
var newdata = $scope.newFun().then(
function(data1)
{
//console.log(data1);
return data1;
});
console.log(newdata);
});
</script>
</body>
</html>
Here i am trying to return the data got from the then function and assign it to a variable. But i am getting a $$ state object, which has a value key which holds the data. Is directly assigning the value is possible or inside the then function i need to use scope object and then access the data??
Many problems with your code.. To start with: you can't return from asynchronous operations, you need to use callbacks for this. In your case since you are using promises use then API of it. Inside of its callback you would assign your data to variable. Angular will do the rest synchronizing scope bindings (by running new digest).
Next problem: don't use $q.defer(), you simply don't need it. This is the most popular anti-pattern.
One more thing: don't make any http requests in controller, this is not the right place for it. Instead move this logic to reusable service.
All together it will look something like this:
var app = angular.module('myApp', []);
app.controller('CartController', function ($scope, data) {
data.get().then(function (data) {
var newdata = data;
});
});
app.factory('data', function($http) {
return {
get: function() {
return $http.get('data.json').then(function (response) {
return response.data;
}, function (err) {
throw {
message: 'Repos error',
status: err.status,
data: err.data
};
});
}
};
});
I'm trying to display the result of a promise in the view, but I get this exception. Other cases of this exception I've found on Google / SO are caused by mistakes I don't see in my code.
I've verified I am using promises, I am resolving the promise inside of the function passed to $timeout, I am returning the promise from the function getData() and not the function that resolves the promise.
Thanks in advance.
Viewer
<html ng-app="controller" ng-controller="MyController as controller">
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.16/angular.min.js"></script>
<script type="text/javascript" src="controller.js"></script>
<script type="text/javascript" src="services.js"></script>
</head>
<body>
{{data}}
</body>
</html>
Controller (controller.js)
angular.module('controller', ['services'])
.controller('MyController', ['MyService', function(MyService) {
MyService.getData().then(function(data) {
$scope.data = data;
});
}]);
Service (services.js)
angular.module('services', [])
.factory('MyService', function($q, $timeout){
var getData = function getData() {
var deferred = $q.defer;
$timeout(function () {
deferred.resolve('Foo');
}, 5000);
return deferred.promise;
};
return {
getData: getData
};
});
Exception stack trace
TypeError: Cannot read property 'then' of undefined
at new <anonymous> (controller.js:5)
at Object.e [as invoke] (angular.js:4219)
at $get.x.instance (angular.js:8525)
at angular.js:7771
at q (angular.js:334)
at M (angular.js:7770)
at g (angular.js:7149)
at angular.js:7028
at angular.js:1460
You are not getting deferred object properly, instead deferred variable holds the function reference, you missed invocation of defer constructor.
var deferred = $q.defer();
^___Missing this
also note that you could just return $timeout as is, since it returns a promise. In your real case if you are using $http just return $http promise instead of creating a redundant promise object with deferred pattern.
.factory('MyService', function($q, $timeout){
var getData = function getData() {
return $timeout(function () {
return 'Foo' ;
}, 5000);
};
return {
getData: getData
};
I'm in a spot where I need to return a promise, that would otherwise be returned by a server http request, and I need it to be rejected, so I don't needlessly hit the server. I've been trying to make and return a rejected promise, something like:
var promise = $q.defer().promise;
return promise.reject( value ); // doesn't work
or
return $q.reject(); // also doesn't work
I can't seem to figure out how to return a rejected promise so I can save the server call, but I'm in a spot where it is either return a made-up rejected promise or make the server call. I'm in between both where I'm not in the original child directive that invoked the call, and the child wasn't able to make the comparison that was possible in the parent directive. Is there anyway to do this without a $timeout?
I think what you are looking for is
var app = angular.module('my-app', [], function() {
})
app.controller('AppController', ['$scope', '$q',
function($scope, $q) {
$scope.message = 'This is for testing';
function test() {
//create a deferred object
var deferred = $q.defer();
//reject the promise
deferred.reject(2);
//then return the promise
return deferred.promise;
}
test().then(function() {
$scope.message = 'Resolved';
}, function() {
$scope.message = 'Rejected';
})
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app" ng-controller="AppController">
{{message}}
</div>
The deffered api has the reject/resolve method, where as the promise api has methods used to register the callback methods
There's no problem with populating a service (factory actually) with asynchronous data. However, what is the proper way of updating data in a service?
The problem that I run into is that all async data is access with .then() method, basically a promise resolve. Now, how would I put something into a service, and update related views?
The service I'm using:
function ($q) {
var _data = null;
return {
query: function (expire) {
var defer = $q.defer();
if (_data) {
defer.resolve(response.data);
} else {
$http.get('/path').then(function (response) {
defer.resolve(response.data);
});
}
return defer.promise;
}
,
byId: function(id) {
var defer = $q.defer();
this.query().then(function(data){
angular.forEach(data, function(item) {
if (item.id == id) {
return defer.resolve(item);
}
});
return defer.reject('id not found');
});
return defer.promise;
}
,
add: function(item) {
...
}
};
}
What would be good implementation of add method? Note, that I'm working with Angular >1.2
I've posted a few examples to show ways to get data from your service into your controllers and thereby allow the data to be bound in the views.
http://plnkr.co/edit/ABQsAxz1bNi34ehmPRsF?p=preview
The HTML
<!DOCTYPE html> <html>
<head>
<script data-require="angular.js#*" data-semver="1.2.4" src="http://code.angularjs.org/1.2.3/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script> </head>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
{{sharedData.label}}
<br>
<input type="text" ng-model="sharedData.label"/>
</div>
<div ng-controller="MyCtrl2">
<input type="text" ng-model="sharedData.label"/>
<button ng-click="updateValue()">test</button>
</div>
<div ng-controller="MyCtrl3">
<input type="text" ng-model="sharedData.label"/>
<button ng-click="updateValue()">test</button>
</div>
<div ng-controller="MyCtrl4">
<input type="text" ng-model="sharedData.label"/>
</div>
</body>
</html>
The JS
angular.module("myApp", []).service("MyService", function($q) {
var serviceDef = {};
//It's important that you use an object or an array here a string or other
//primitive type can't be updated with angular.copy and changes to those
//primitives can't be watched.
serviceDef.someServiceData = {
label: 'aValue'
};
serviceDef.doSomething = function() {
var deferred = $q.defer();
angular.copy({
label: 'an updated value'
}, serviceDef.someServiceData);
deferred.resolve(serviceDef.someServiceData);
return deferred.promise;
}
return serviceDef;
}).controller("MyCtrl", function($scope, MyService) {
//Using a data object from the service that has it's properties updated async
$scope.sharedData = MyService.someServiceData;
}).controller("MyCtrl2", function($scope, MyService) {
//Same as above just has a function to modify the value as well
$scope.sharedData = MyService.someServiceData;
$scope.updateValue = function() {
MyService.doSomething();
}
}).controller("MyCtrl3", function($scope, MyService) {
//Shows using a watch to see if the service data has changed during a digest
//if so updates the local scope
$scope.$watch(function(){ return MyService.someServiceData }, function(newVal){
$scope.sharedData = newVal;
})
$scope.updateValue = function() {
MyService.doSomething();
}
}).controller("MyCtrl4", function($scope, MyService) {
//This option relies on the promise returned from the service to update the local
//scope, also since the properties of the object are being updated not the object
//itself this still stays "in sync" with the other controllers and service since
//really they are all referring to the same object.
MyService.doSomething().then(function(newVal) {
$scope.sharedData = newVal;
});
});
Regarding the add method in the service you'd want it to do something similar to a get, just create a deferred that you return the promise from and then do your async business (http request). For your byId function you may want to use a cached version (save the data that comes back from the query call in a property of the service). This way the query doesn't need to be executed every time if that's not necessary.