AngularJS factory not working - angularjs

I have abstracted my working code from a controller into a factory, but it doesn't seem to be working and I can't find what's wrong. I opted for a factory rather than a service because I wanted to execute some code that defined the variable before returning that variable; I want to get result.station (a part of the data returned by the API), not the full result.
This is my code:
var app = angular.module("myApp", []);
app.factory('api', ['$http',
function($http) {
var station_list = [];
$http({
method: 'GET',
url: 'http://api.irail.be/stations/?format=json&lang=nl'
})
.success(function(result) {
station_list = result.station;
});
return {
Stations: function() {
return station_list;
}
};
}
]);
app.controller("myController", ['api', '$scope',
function(api, $scope) {
$scope.station_list = api.Stations();
$scope.title = "Stations";
}
]);
and a working example.

Try this:
.success(function(result) {
angular.copy(result.station, station_list);
});

You had a small error, you were replacing the array instead of populating it. I used angular.copy instead of the assignment in your factory and it works
http://plnkr.co/edit/sqgKcFZAcClmkfdXHhrz

The problem is that you are dealing with asynchronous nature of AJAX.
I would suggest to have a delegate method in controller, which will be called when the service call is complete.
Something like the following:
app.controller("myController", ['api', '$scope',
function(api, $scope) {
api.Stations( function(station_list) {
$scope.station_list = station_list;
});
$scope.title = "Stations";
}
]);
The following is a service method excerpt:
return {
Stations: function(delegate) {
if (delegate)
delegate(station_list);
return station_list;
}
};

Related

Resource file is undefined on scope

I have a resource file that sometimes is read, and sometimes is undefined and crashes all the application. I do not know why it only works sometimes and it is causing me trouble to figure a solution out. On my controllers I am passing the service that I created to read the file which is appResourcesService:
app.controller('CommonController', ['$scope', '$location', 'appResourcesService',
function ($scope, $location, appResourcesService)
And the service to read resoruces is the following:
'use strict';
app.factory('appResourcesService', ['$resource', function ($resource) {
var appResourcesServiceFactory = {};
appResourcesServiceFactory.getResources = function ($scope, language, locale) {
var languageFilePath = 'resources/AppResources.json';
$resource(languageFilePath).get().$promise.then(function (data) {
$scope.appResources = data;
}, function (reason) {
var defaultLanguageFilePath = 'resources/AppResources.json';
$resource(defaultLanguageFilePath).get(function (data) {
$scope.appResources = data;
});
});
};
return appResourcesServiceFactory;
}]);
Like I said, sometimes it works without any kind of problem, but sometimes when in the controller I reach the piec eof code $scope.appResources, appResources is undefined. I think it is maybe because it reaches there before the file is read, but I am not being able to find a solution to avoid it.
Your factory should return a promise.
It's the job of your controller to handle data. (I use $http as it's cleaner)
app.factory('appResourcesService', ['$http', function($http) {
var appResourcesServiceFactory = {};
appResourcesServiceFactory.getResources = function () {
var languageFilePath = 'resources/AppResources.json';
return $http.get(languageFilePath).then(function(response) {
return response.data;
});
)};
return appResourcesServiceFactory;
}]);
You resolve the promise inside the controller with .then to get the data and set your scope.
appResourcesServiceFactory.getResources().then(function(data) {
$scope.appResources = data;
})

AngularJS Ctrl As Syntax with Services

I'm migrating my Angular 1.x code to use the newer, more preferred syntax of avoiding using $scope and using the controller as syntax. I'm having an issue with getting data from a service though.
Here's my example:
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
this.name = 'John Doe';
// this.userData = {
// }
this.userData = userService.async().then(function(d) {
return d;
});
console.log(this.userData);
}
]);
myApp.service('userService', function($http) {
var userService = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('https://jsonplaceholder.typicode.com/users').then(function(response) {
// The then function here is an opportunity to modify the response
// console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return userService;
});
<body ng-app="myApp">
<div id="ctrl-as-exmpl" ng-controller="MainCtrl as mainctrl">
<h1>Phone Numbers for {{mainctrl.name}}</h1>
<button ng-click="">newest</button>
<p>{{mainctrl.userData}}</p>
<ul>
<li ng-repeat="users in mainctrl.userData">{{users.phone}} -- {{users.email}}</li>
</ul>
</div>
</body>
This issue I'm having is that the data returned from the userService.async is an object that I can't seem to drill down thru to get the data from, as the data is a child of $$state, which can't seem to be used in the view.
If this isn't the proper way to use services in the Controller As/$scope-less syntax, what is the proper way?
Live example here: http://plnkr.co/edit/ejHReaAIvVzlSExL5Elw?p=preview
You have a missed an important part of promises - when you return a value from a promise, you return a new promise that will resolve to this value.
The current way to set the data returned from a promise to a local variable in your controller is this:
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
this.name = 'John Doe';
userService.async().then(function(d) {
this.userData = d;
});
}
]);
But now you are in a catch because of the scope of this, so it is common to use a "placeholder" variable for the controller this scope.
myApp.controller('MainCtrl', ['$http', '$location', 'userService',
function($http, $location, userService) {
var $ctrl = this; // Use $ctrl instead of this when you want to address the controller instance
$ctrl.name = 'John Doe';
userService.async().then(function(d) {
$ctrl.userData = d;
});
}
]);
this.userData = userService.async().then(function(d) {
return d;
});
In this bit of code this.userData is actually being defined as a promise. If you want the data that is being returned, you need to use the d parameter from your .then function like so:
userService.async().then(function(d){
this.userData = d;
//a console.log() here would determine if you are getting the data you want.
});
Edit
I just noticed in your Service that you are already using .data on your response object.
Honestly, my best advice to you would be to just return the promise from the http.get() call inside of your service like this:
myApp.service('userService', function($http) {
var userService = {
async: function() {
return $http.get('https://jsonplaceholder.typicode.com/users');
}
};
return userService;
});
And then you could use something like this to capture and utilize the response data:
userService.async().then(function(response){
this.userData = response.data;
);
This may be a bit cleaner of a solution

How to call a controller from another file in AngularJS?

Let's say I have the following files:
JS1 file:
var app = angular.module('myApp', []);
app.controller('myCtrlOfJs1', function ($scope, $http) {
$http.post("url", data).success(function(data, status) {
$scope.hello = data;
})
})
JS2 file:
var app = angular.module('myApp', []);
app.controller('myCtrlOfJs2', function ($scope, $http) {
// $http.post("url", data).success(function(data, status) {
// $scope.hello = data;
// })
})
What I don't want is rewrite the same code twice. Then I want to call that function in my JS1 file, in the JS2 file. Is it possible?
PS: It's only an example, my code has much more lines than it.
Any help is appreciated. Thanks.
You can always use services: https://docs.angularjs.org/guide/services
app.factory('commonFeatures', function($http) {
return {
setHelloMsg: function(scope) {
$http.post("url", data).success(function(data, status) {
scope.hello = data;
})
}
};
});
app.controller('myCtrlOfJs1', function ($scope, commonFeatures) {
commonFeatures.setHelloMsg($scope);
});
edit:
As a response to your comment:
Well, it can be located nearly everywhere. Have a look at https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#single-responsibility. There are more style guides, but I personally follow this one and it just makes sense.
Your view doesn't have to change at all.
In general, you want to separate common functionality like this out into angular services: https://docs.angularjs.org/guide/services
In this example it might look like this:
var andyModule = angular.module('anyModule', []);
andyModule.factory('helloDataService', function($http) {
var helloDataService = {
// returns a promise
getHelloData = function() {
return $http.post("url", data)
}
};
return shinyNewServiceInstance;
});
And in your controller
app.controller('myCtrlOfJs1', function ($scope, $http, helloDataService) {
helloDataService.getHelloData().success((data)=> {
$scope.data = data;
})
})
I would abstract this to a service. Services are particularly good spots for things like network calls.
By the way using angular.module("app", modules) syntax will create an app named app, so JS2 file will overwite the module from JS1. I doubt you intend this.
angular.module("app", []);
var app = angular.module('myApp');
app.controller('myCtrlOfJs1', function ($scope, hello) {
hello.post().then(data => scope.$hello = data);
});
var app = angular.module('myApp');
app.controller('myCtrlOfJs2', function ($scope, hello) {
hello.post().then(data => scope.$hello = data);
});
app.factory("hello", function ($http) {
// set data somehow
return {
post() {
return $http.post("url", data);
},
};
});
This still duplicates the scope.$hello code which may be what you want to avoid. In this case you can extend from another controller that implements this either using newer class syntax or through the prototype. In this case I think it would be better to use controllerAs instead of $scope.
This is a method you can achieve what you are looking for.
I prefer the service method though
Take a look at this :
use case for $controller service in angularjs
https://docs.angularjs.org/api/ng/service/$controller
Hope it helps

Angularjs cannot get data from service

I'm trying to pass data from one controller to another using a service, however no matter what I'm trying it always returns 'undefined' on the second controller. Here is my service :
app.service('myService', ['$rootScope', '$http', function ($rootScope, $http) {
var savedData = {}
this.setData = function (data) {
savedData = data;
console.log('Data saved !', savedData);
}
this.getData = function get() {
console.log('Data used !', savedData);
return this.savedData;
}
}]);
Here is controller1 :
.controller('HomeCtrl', ['$scope','$location','$firebaseSimpleLogin','myService','$cookies','$window', function($scope,$location, $firebaseSimpleLogin, myService, $cookies, $window) {
loginObj.$login('password', {
email: username,
password: password
})
.then(function(user) {
// Success callback
console.log('Authentication successful');
myService.setData(user);
console.log('myservice:', myService.getData()); // works fine
}]);
And then controller2:
// Dashboard controller
.controller('DashboardCtrl', ['$scope','$firebaseSimpleLogin','myService',function($scope,$firebaseSimpleLogin, $location, myService) {
console.log('myservice:', myService.getData()); //returns undefined
}]);
That is simple code, unfortunately I've been struggling for a few hours now, any suggestion ? Thanks.
Created a fiddle here:
http://jsfiddle.net/frishi/8yn3nhfw/16
To isolate the problem, can you remove the dependencies from the definition for myService and see if that makes it work? Look at the console after you load the fiddle.
var app = angular.module('app', [])
.service('myService', function(){
this.getData = function(){
return "got Data";
}
})
I assume the issue is that you are returning this.savedData in the service. Try returning savedData.
this behaves different in Javascript than in other languages.

Problems using $http inside a Service

I have a basic data Service which will be used across Controllers. But I'm having an issue grabbing some data that's been added via $http.
Service:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
});
}]);
Controller:
angular.module('core').controller('SignupController', ['$scope', '$http', '$state', 'FormService', function($scope, $http, $state, FormService) {
console.log(FormService.dropdownData); // Shows full object incl industries
console.log(FormService.dropdownData.industries); // empty object {}
}]);
How do I get FormService.dropdownData.industries in my controller?
Create a service like below
appService.factory('Service', function ($http) {
return {
getIndustries: function () {
return $http.get('/json').then(function (response) {
return response.data;
});
}
}
});
Call in controller
appCtrl.controller('personalMsgCtrl', ['$scope', 'Service', function ($scope, Service) {
$scope.Industries = Service.getIndustries();
}]);
Hope this will help
Add a method to your service and use $Http.get inside that like below
_this.getindustries = function (callback) {
return $http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
callback(_this.dropdownData)
});
};
In your controller need to access it like below.
angular.module('core').controller('myController', ['$scope', 'FormService', function ($scope, FormService) {
FormService.getDropdownData(function (dropdownData) {
console.log(dropdownData); // Shows full object incl industries
console.log(dropdownData.industries); // object {}
});
} ]);
Given that your console log shows the correct object, that shows your service is functioning properly. Only one small mistake you have made here. You need to access the data attributes in your return promise.
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
//note that this is resp.data.industries, NOT resp.industries
_this.dropdownData.industries = resp.data.industries;
});
}]);
Assuming that you're data is indeed existing and there are no problems with the server, there are quite a few possible solutions
Returning a promise
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries = $http.get('/json');
}]);
//Controller
FormService.industries
.then(function(res){
$scope.industries = res.industries
});
Resolving with routeProvider / ui-route
See: $http request before AngularJS app initialises?
You could also write a function to initialize the service when the application starts running. At the end of the day, it is about waiting for the data to be loaded by using a promise. If you never heard about promises before, inform yourself first.
The industries object will be populated at a later point in time when the $http call returns. In the meantime you can still bind to the reference in your view because you've preserved the reference using angular.copy. When the $http call returns, the view will automatically be updated.
It is also a good idea to allow users of your service to handle the event when the $http call returns. You can do this by saving the $promise object as a property of industries:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries.$promise = $http.get('/json').then(function(resp){
// when the ansyc call returns, populate the object,
// but preserve the reference
angular.copy( resp.data.industries, _this.dropdownData.industries);
return _this.dropdownData.industries;
});
}]);
Controller
app.controller('ctrl', function($scope, FormService){
// you can bind this to the view, even though the $http call has not returned yet
// the view will update automatically since the reference was preserved
$scope.dropdownData = FormService.dropdownData;
// alternatively, you can hook into the $http call back through the $promise
FormService.dropdownData.industries.$promise.success(function(industries) {
console.log(industries);
});
});

Resources