$scope variable value not updated in controller Angular JS? - javascript

Below added my controller code. I want to access value of $scope.FCGId ....... How can access this variable?
angular.module('hotelApp.controllers')
.controller('menuCtrl', ['$scope','menu'
function($scope,'menu') {
$scope.categories = [];
$scope.FCGId = 0
$scope.items = [];
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
});
}
$scope.getItems = function(gropuId) {
menu.getItems(gropuId).success(function(data) {
$scope.items = data;
console.log(data);
});
}
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
$scope.getItems($scope.FCGId);
});
}
]);
From, above code returns 0 instead of value updated in getCategories() function.

Well
$scope.getCategories function is giving asynchronous call
in below event
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
$scope.getItems($scope.FCGId);
});
when you call $scope.getCategories(), this asynchronous call is given.
But script is not waiting for completion of that call. And script access $scope.FCGId variable in console.log($scope.FCGId); without initialization because asynchronous cal is not completed.
Solution to this.
Either you call $scope.getCategories function at the start of controller as initialization part
or you should return promise from $scope.getCategories function
or use promise in another way as per your requirement.
EDIT CODE.
Defined $scope.getCategories as follow
inejct $q in your controller.
var defer = $q.defer();
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
// $scope.FCGId = data['rows'][0].GRPCOD;
defer.resolve(data['rows'][0].GRPCOD);
return defer.promise;
});
}
and event handling in this way
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories().then(function(successData){
$scope.FCGId = successData
console.log($scope.FCGId);
});
$scope.getItems($scope.FCGId);
});
Solution -2.
Also there is no dependency while giving call to $scope.getCategories function
so you can call it at the starting of comptroller.
Same you can do for the call to $scope.getItems.

Your problem happens because javascript almost always runs faster than asynchronous call returns, so your $scope.getItems always calls before $scope.getCategories returns.
To strictly order the API calls you need a powerful construct called promise. There should be a lot of resources out there, just google "angular promise" and you're good =)
Edit: Actually making use of the success function is the most straight forward way to do this
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
$scope.getItems($scope.FCGId); // to here
});
}
$scope.getItems = function(gropuId) {
menu.getItems(gropuId).success(function(data) {
$scope.items = data;
console.log(data);
});
}
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
// $scope.getItems($scope.FCGId); // move this line
});
By this way you don't have to deal with all those $q and d's. And promise-antipatterns.

Seems like your menu.getCategories() is an asynchronous execution block, and because of deferred execution, you are getting 0 as the value of $scope.FCGId.
You can pass a function as the second parameter to the getCategories function, that will get executed and perform necessary assignments and further calls.
$scope.setFCGValue = function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
$scope.getItems($scope.FCGId);
};
$scope.$on('$ionicView.afterEnter', function() {
menu.getCategories().success($scope.FCGValue);
});
What we are doing is passing our custom function, that will be executed after the getCategories() part.

Related

Javascript Promises on Angular Controller

I don't familiar with JavaScript Promises function.
Currently I have this code on my Angular Controller
$http.get('pages/about.html').then(function(response) {
var raw_html = response.data;
$scope.aboutHTML = raw_html.replace(/</g,"<");
});
I want to re-write the code so I could do something like this
$scope.indexHTML = getHTML('pages/index.html');
$scope.aboutHTML = getHTML('pages/about.html');
...
with function like this
function getHTML(url){
$http.get(url).then(function(response) {
var raw_html = response.data;
return = raw_html.replace(/</g,"<");
});
}
How to write the the code properly for the function above?
[Update #1]
Temporary Solution by #charlietfl
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
I wanna to write this function to reduce the manual work, with this way I still need to write down $scope.{page} for each page, so anyone know better way?
[Update #2]
Solution by #joeytwiddle
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
There is no way to just return the result, because the result will not be available until some time in the future. #asynchronous
You can only handle the result using a callback function.
If you want to minimize the work from outside, I would suggest something like this:
getHTMLAndStore('pages/index.html', $scope, 'indexHTML');
getHTMLAndStore('pages/about.html', $scope, 'aboutHTML');
function getHTMLAndStore(url, object, property) {
$http.get(url).then(function(response) {
var raw_html = response.data;
var weird_html = raw_html.replace(/</g,"<");
object[property] = weird_html;
}).catch(console.error.apply(console));
}
This is pure JS and not really related to Angular.
Note that these two requests will fire in parallel, not in sequence.
$http returns a promise so you need to return that promise from the function and use another then() to assign the scope variable:
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
Currently your function doesn't return anything

Returning new Object from Service that uses $resource

I am working on making my existing code a bit better and I would like to have two objects for interacting with a RESTful API.
Previously, I used $http to get get the data and return the promise. I would then .then some actions to it. Then I would requery to get additional data and repeat it. So there were several nested $http queries. I would like to avoid that if possible.
I would like to have a service (or a factory) that I can use to configure various query parameters and another object for massaging and outputting the response.
Here is what I have so far:
var solr1 = angular.module('solr', ['ngResource']);
solr1.run(function(){
console.log("Module Loaded");
});
solr1.service('solrser', function($resource,solrresult) {
console.log("In Solrser")
this.serverurl = 'url';
this.res = $resource(this.serverurl);
this.query = function (p) {
this.res.get(p).$promise.then(function(d) {
return solrresult(d);
});
}
});
solr1.factory('solrresult',function() {
return function(a) {
this.data = a;
this.ready = 0;
console.log("In Factory")
this.getdocs = function() {
console.log("Getting Data")
console.log(this.data);
return this.data.docs; //this is line 9
}
return this;
}});
Controller looks like this:
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
console.log("Start");
var res = solrser.query({'q':'potato'});
console.log(res)
console.log(res.getdocs())
}]);
The output looks like this:
Module Loaded solr_module.js:5
In Solrser solr_module.js:9
Start main_controller.js:6
undefined main_controller.js:9
TypeError: Cannot read property 'getdocs' of undefined
at new <anonymous> (.........../main_controller.js:10:21)
at Object.e [as invoke] (https://code.angularjs.org/1.3.1/angular.min.js:36:365)
at F.instance (https://code.angularjs.org/1.3.1/angular.min.js:75:91)
at https://code.angularjs.org/1.3.1/angular.min.js:58:287
at s (https://code.angularjs.org/1.3.1/angular.min.js:7:408)
at G (https://code.angularjs.org/1.3.1/angular.min.js:58:270)
at g (https://code.angularjs.org/1.3.1/angular.min.js:51:172)
at g (https://code.angularjs.org/1.3.1/angular.min.js:51:189)
at https://code.angularjs.org/1.3.1/angular.min.js:50:280
at https://code.angularjs.org/1.3.1/angular.min.js:18:8 angular.js:11339
In Factory solr_module.js:25
So the factory gets created after the controller resumes execution. What am I doing wrong here?
You have a timing issue.
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
console.log("Start");
var res = solrser.query({'q':'potato'});
console.log(res)
console.log(res.getdocs())
}]);
When you call solrser.query() it runs async to get the data. So the res is not populated with any real value until after the promise returns inside the "solrser" service. You would need to do something more like:
solrser.query({'q':'potato'},function(res) {
console.log(res);
console.log(res.getdocs());
}); // could be done as a promise as well....
Separately, you also are doing something really strange when the response is called inside the solrser service.
this.query = function (p) {
this.res.get(p).$promise.then(function(d) {
return solrresult(d);
});
}
Calling return from within the promise handler is not going to do you very much. You need to either call a callback or resolve a deferred. Something like this would work better:
this.query = function (p,callback) {
this.res.get(p).$promise.then(function(d) {
callback(solrresult(d));
});
}
Or as a promise
this.query = function (p) {
var defer = $q.defer();
this.res.get(p).$promise.then(function(d) {
defer.resolve(solrresult(d));
});
return defer.promise;
}
Or you could just pass the get(p).$promise through, etc.
UPDATE:
Since this is an async action, there will always be a promise or callback. You have to do one of:
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
var res = solrser.query({'q':'potato'},function() {
res.getDocs();
});
}]);
Or just straight callback
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
solrser.query({'q':'potato'},function(res) {
res.getDocs();
});
}]);
I see missing semicolon
console.log("In Solrser")
shouldbe
console.log("In Solrser");
also missing semicolon here
console.log("Getting Data")
console.log("In Factory")
in controller you have missing semicolons too.
I think this is a reason why your code don't work.
To avoid bug like missing semicolon you can use JSHint plugin in your IDE

How to return data in a factory method within Angular?

I'm an ionic/Angular n00b and I having trouble wrapping my head around how to do this.
I have a factory defined as such:
angular.module('starter.services', [])
.factory('Calendars', function () {
var calendars;
var success = function(message) {
calendars = message;
return calendars;
};
var error = function(message) {alert("Error: " + message)};
window.plugins.calendar.listCalendars(success,error);
return {
all: function() {
return calendars;
},
get: function(calendarId) {
return calendars[calendarId];
}
}
});
And I'm trying to retrieve the calendars within my controller like this:
.controller('CalendarsCtrl', function($scope,Calendars) {
$scope.calendars = Calendars.all();
})
The factory method is being called but the results are not available until the 'success' callback is invoked so the CalendarsCtrl is always undefined.
How to solve this?
Edit - I've corrected the call within the controller. The same issue remains though, that the function does not return results until the success callback.
You will have to use a promise.
First add the dependency $q
.factory('Calendars', function ($q) {
then in all() you do this
all: function() {
var deferred = $q.defer();
window.plugins.calendar.listCalendars(function(data) {
deferred.resolve(data);
}
,function(error) {
deferred.reject(error); // something went wrong here
});
return deferred.promise;
now this will make the return after the data has been resolved (no more undefined).
One last thing, now when you get the data back at your controller you do this
var promise = Calendars.all();
promise.then(function(data) {
console.log('Success: you can use your calendar data now');
}, function(error) {
console.log('Failed for some reason:' + error);
});
You can read some more about promises here: https://docs.angularjs.org/api/ng/service/$q
I know it's hard to grasp the first time.
Angular factories are returning an object, in order to call their methods you must call them with Calendar.all() which will invoke the inner function.

Angular + requirejs - the controller cannot pick up ajax return data from the factory?

My controller cannot pick up the data that processed from $http.get() in requirejs environment. Any idea how can I fix it?
UserFactory.js
define(['app'], function (app) {
app.module1.factory('UserFactory', ['$http', function($http) {
//your minsafe controller
var factory = {};
factory.getUsers = function(){
$http.get('api.php').success(function(data) {
// here the data from the api is assigned to a variable named users
return data; // [{"id":"1","name":"Big Kev","email":"bigkev#example.com"},{"id":"2","name":"Judy","email":"punk#example.net"},{"id":"3","name":"Suzy","email":"suzy#example.com"},{"id":"4","name":"Joey","email":"sheena#example.org"},{"id":"5","name":"DeeD","email":"deedee#example.net"}]
});
};
return factory;
}]);
});
view.js,
define(['app'], function (app) {
app.module1.controller('View1Ctrl', ['$scope','UserFactory', function($scope,userFactory) {
//your minsafe controller
$scope.message = "Message from View1Ctrl";
$scope.users = userFactory.getUsers();
console.log($scope.users); // undefined
}]);
});
as you can see, I get undefined for $scope.users. It seems that console.log($scope.users); is executed before $http.get('api.php') in the factory. How can I fix this then?
This has nothing to do with Require; it is a very common misconception about how asynchronous code runs (especially promises).
You see factory.getUsers() makes an asynchronous call. The response will be available at a later time and success will be called with the response data at that later time. On the other hand, $scope.users = userFactory.getUsers() is synchronous. It is almost guaranteed that the async call wll NOT have returned by the time the next statement runs. And even if it has returned, the callback will NOT have run because Javascript is single threaded in the browser.
Moreover you have another serious fault: the success callback is "procedural" in the sense that it receives the response and has to do something with it. Its return value is disregarded!
To use promises (or at least $http) correctly in this case, change factory.getUsers to return the $http promise:
factory.getUsers = function(){
return $http.get('api.php');
};
And change the controller to use the promise or the success/error callbacks:
// option (1) with promise
userFactory.getUsers().then(function(response) {
$scope.users = response.data;
});
// option (2) callback
userFactory.getUsers().success(function(data) {
$scope.users = data;
});

To whom return is returning the value (javascript)?

I saw following code in the HotTowel project. In the following code, callback method for then returns value return vm.messabeCount = data;
(function () {
'use strict';
function dashboard(common, datacontext) {
vm.messageCount = 0;
function getMessageCount() {
return datacontext.getMessageCount().then(function (data) {
/******* Here ********/
return vm.messageCount = data;
});
}
}
})();
Am wondering why & to whom it's returning value. Is it some standard practice? Can't the code be just.
return datacontext.getMessageCount().then(function (data) {
vm.messageCount = data;
});
Or
return datacontext.getMessageCount().then(function (data) {
vm.messageCount = data;
return;
});
getMessageCount is a function returning a promise object. then method of this promise returns another promise again. It makes possible to chain multiple then parts. Each then(function() { ... }) has an ability to modify a data to be passed to the next then invocation. So this construction:
return datacontext.getMessageCount().then(function(data) {
return vm.messageCount = data;
});
means to modify a data passed to resolve callbacks. Without this return success functions would be resolved with undefined value, while we need it to be resolved with data.

Categories

Resources