I'm trying to use a 1.5 component with AngularJS. I have a service that gets my JSON file using $HTTP and returns the promise. I then resolve the promise in my components controller and assign it to a value on the controller using this.work. Although this doesn't show in my HTML page. I have included comments in the code to explain a little better if this is confusing.
It's my understanding that the resolving of the promise (in my controller) is happening asynchronously but once it's resolved why am I not shown the updated changed to the variable $ctrl.work in the view. Instead I never get a value from the variable.
// Component
(function () {
angular.module('development')
.component('pgDev', {
templateUrl: 'app/development/development.template.html',
controller: ['workList', function (workList) {
//this.work = 'HELLO WORLD'; // <- this shows up in the html if uncommented
workList.getWorkItems().then(function (d) {
console.log(d.test); // outputs: myjsonfile
this.work = d.test; // <- this doesnt show in the html
});
}]
})
}());
// HTTP Get Service
(function () {
angular.module("development").factory("workList", ["$http",
function ($http) {
return {
getWorkItems: function () {
return $http.get("data/worklist/worklist.json").then(function (d) {
return d.data;
});
}
}
}])
})();
// html
workitems: {{$ctrl.work}}
You are loosing context of execution which means that this inside your callback function if not and instance of component controller. A simple (and modern) solution is to use arrow function instead of normal anonymous:
workList.getWorkItems().then(d => {
this.work = d.test;
});
Related
I'm trying to get some data from a Service into a Controller and I keep get an undefined variable.
angular
.module("classes")
.service("MyService", function ($http) {
this.foo;
$http.get("/classes/all").then(function (response) {
this.fighters = response.data;
this.foo = this.fighters;
console.log(this.foo);
});
console.log(this.foo);
})
When I run this I get on the console, by this order, line 11 is undefined and then line 9 returns me the array.
And when in the controller I try to get the variable foo, it also says undefined.
$scope.fooFighters = MyService.foo;
The reason is because of asynchronous execution of your API call. I would suggest you to rewrite the code to use a factory that will return a promise object. No need to bring unnecessary variables.
angular.module("classes").factory("MyService", function($http) {
return {
fighters: function() {
return $http.get("/classes/all").then(function(response) {
return response.data;
});
}
}
})
And in your controller, you can get the value by injecting the service in the controller and then by referencing it like
MyService.fighters().then(function(data){
$scope.fooFighters = data;
});
because it will take some time to load the ajax/http request data after that line 9 will work. So if you want to work with ajax/http data then you should write code/function inside
$http.get("/classes/all").then(function (response) {
// do something
});
I have set up two controllers (Controller A and Controller B) and a service (Service). I am attempting to sync the data from controller A to the service, and present that information to Controller B.
Within my Service, I've established a variable confirmdata and get and set functions:
function setData(data) {
confirmdata = angular.copy(data);
}
function getData() {
return confirmdata;
}
In controller A I've created a function syncto sync information from the controller to the service:
this.sync = function () {
var data = {
payment: this.getpayment()
}
Service.setData(data);
In controller B I've assigned a function as:
this.sync = function () {
this.viewData = Service.getData();
console.log('TestingData', this.viewData);
For a reason I am unaware of; my console log simply returns undefined when it should be returning the results of the getpayment() function. Am I missing something here?
The fact that you are getting undefined would indicate that you haven't initialized 'confirmdata' in your service. Whether this is the actual issue though, isn't clear. For a simple example, I would design your service like this:
myApp.factory('sharedService', [function () {
var confirmdata = {};
return {
setData: function (newData) { confirmdata = newData; },
getData: function getData() { return confirmdata; }
}
}]);
Take a look at this plunker. It gives an example of data being shared between controllers via a service.
I'm trying to retrieve a list of options from our database and I'm trying to use angular to do it. I've never used services before but I know that's going to be the best way to accomplish what I want if I'm going to use data from my object in other controllers on the page.
I followed a couple tutorials and put together a factory that makes an http request and returns the data. I've tried several ways of doing it, but for some reason nothing is happening. It's like it never runs the factory function and I can't figure out why.
Factory:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {};
locaService.locations = {};
var resorts = {};
locaService.getLocations=
function() {
$http.get('/url/url/dest/').success(function (data) {
locaService.locations = data;
});
return locaService.locations;
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
$scope.resort;
$scope.locations= locaService.getLocations();
}]);
I just want the data to be returned and then assigned to the $scope.locations to be used for ng-options in the view. Then I want my other function to run on click for the next field to be populated by the variable resort. How would I do this? Any help would be great! Thanks!
$http service returns a promise, and your function should return that promise. Basically your getLocations function should be something like the following
locaService.getLocations=
function() {
return $http.get('/url/url/dest/');
};
Then in your controller you should retrieve the options using this promise:
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
Using jquery in controllers or manipulating dom elements in controllers is not a good practice, you can apply styles and css classes directly in views using ng-style or ng-class.
Here is an example how all it should look wired up:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {
locations: {}
};
var resorts = {};
locaService.getLocations= function() {
return $http.get('/url/url/dest/');
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
/* Apply these styles in html using ng-style
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
*/
$scope.resort;
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
}]);
Below is the recommended way to get data into a controller from a factory using $http -- according to https://github.com/johnpapa/angularjs-styleguide
What i don't get is how the two success callbacks on $http work (i commented what i think the two callbacks are).
1) What is the point of the first callback?
2) Where does vm.avengers point to? Is it a reference to another object?
3) is 'data' in the second callback = 'response.data.results' from the first?
4) I'm counting 3 total callbacks chained, is that correct?
P.S. i already know about promises, but want to learn this pattern specifically
The factory
/* recommended */
// dataservice factory
angular
.module('app.core')
.factory('dataservice', dataservice);
dataservice.$inject = ['$http', 'logger'];
function dataservice($http, logger) {
return {
getAvengers: getAvengers
};
function getAvengers() {
return $http.get('/api/maa')
.then(getAvengersComplete)
.catch(getAvengersFailed);
//Callback One
function getAvengersComplete(response) {
return response.data.results;
}
function getAvengersFailed(error) {
logger.error('XHR Failed for getAvengers.' + error.data);
}
}
}
The Controller
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
activate();
function activate() {
return getAvengers().then(function() { //Callback 3
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers()
.then(function(data) { //Callback 2
vm.avengers = data;
return vm.avengers;
});
}}
The point of this first callback is to do any manipulation with the data prior to it entering into the app and to actually pull the useful data out of the http response object.
vm.avengers is declared at the top of your controller. It's using the "controller as" syntax and is being put on a reference to the "this" object of the controller. You're ultimately using vm.avengers to access the data in the view.
Correct.
HTTP Call -> getAvengersComplete -> getAvengers, so correct 3 callbacks.
I am trying to have a $watch on a scope so I Can listen for changes, however, I do not want the watch to start listening until the data has come in and populated. I am using a $q factory and then to populate the items, then I want the watch to start listening after everything is populated. I can't seem to get down how to control these order of events.
SO I have in my controller -
//call to the $q factoy to execut all http calls
getDefaults.resource.then(function(data){
//fill in scopes with data
$scope.allAccounts = data[0].data.accounts;
//THEN watch the scope for changes
$scope.$watch('selectedResources', function (newValue) {
//do action on change here
});
}
SO I'm wondering if there is any way to control these order of events in angular. Thanks for reading!
You can create a service for your xhr calls:
.factory('xhrService', function ($http) {
return {
getData: function () {
return $http.get('/your/url').then(cb);
}
};
function cb(data) {
/// process data if you need
return data.accounts;
}
});
after that you can use it like this inside your controller:
.controller('myController', function (xhrService) {
$scope.allAccounts = [];
xhrService.getData()
.then(function (accounts) {
$scope.allAccounts = accounts;
return $scope.allAccounts;
})
.then(function () {
$scope.$watch('allAccounts', function (newValue) {
// do something
}
});
});
I think this is a good way to structure your code because you can reuse your service and you can add (or not) any watch you need (inside any controller)
And the most important, from the docs: https://docs.angularjs.org/api/ng/service/$q - "$q.then method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback" - this is why each then callback needs a return statement.