I am building a simple controller, which also includes a service. This service returns a json. Now, my problem is, when i call the request function via the controller and print out the results, I get all objects twice.
So this is my code right now:
angular.module('clientApp')
.factory('RequestService', ['$http', function($http){
function getData() {
return $http.get('../../mockdata/data.json')
}
return {
getData: getData
};
}])
.controller('MainCtrl', ['RequestService', function(RequestService) {
var data = [];
RequestService.getData()
.success(function(allData) {
data = allData[0];
console.log(allData)
})
}]);
json: http://pastebin.com/dTcNDQeA
I guess, the problem must be somewhere around the .success, but I can`t find out why specifically.
My Solution: Finally it worked fine for me when I wrapped a function around the the service call in the controller. Still figuring out, why.
Try not returning anything from your service.
.service('RequestService', ['$http', function($http){
this.getData = function() {
return $http.get('../../mockdata/data.json')
}
}])
// controller
RequestService.getData().success(function(data){
$scope.data = data;
})
To only call the service once you can see this example
Try this
angular.module('clientApp')
.service('RequestService', ['$http', function($http){
var getData = function (handler) {
$http.get('../../mockdata/data.json').success(handler)
}
return {
getData: getData
};
}])
.controller('MainCtrl', ['RequestService', function(RequestService) {
var data = [];
RequestService.getData(function(allData) {
data = allData[0];
console.log(allData)
});
}]);
You have defined your service as if it were a factory.
Try, either:
change the .service to a .factory
or:
do this.getData = function () { ... }
(as #user2954587 says)
Ideally it should return single object. May be JSON you are getting from backend has problem
Related
How can I access $scope data from view to my factory in angularjs? I can access $scope.items from my controller, but when I need to use it in my factory to use the data and generate a pdf I cannot access it.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
$scope.link = "http://localhost:3450/loading.html";
testFactory.all().then(
function(res){
$scope.link = res;
},
function(err){
console.log(err);
}
);
})
.factory('testFactory', function($q){
var pdfInfo = {
content: [
//data should be here...
]
};
var link = {};
function _all(){
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc){
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I used factory when I click the generate button from my view, it will wait until the pdf is generated. Coz when I did not do it this way before, I need to click the button twice just to get the pdf generated.
You can just pass the data to your factory as a
function parameter.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
var pdfInfo = {
content: $scope.items
};
$scope.link = "http://localhost:3450/loading.html";
testFactory.all(pdfInfo).then(
function(res) {
$scope.link = res;
},
function(err) {
console.log(err);
}
);
})
.factory('testFactory', function($q) {
var link = {};
function _all(pdfInfo) {
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc) {
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I did it. I forgot to send the $scope.items to my factory. So what i did is I added testFactory.all($scope.items) in my controller instead of just plain testFactory.all().
Then in my factory,
I used function _all(value), so I can used the values passed by the views through controller. I am not sure if this is the proper way, but it works. Please suggest good practice if you have.
It is a bad practice to move around $scope to other services, as they may change it and effect your controller logic. It will make a coupling between controllers to other services.
If your factory requires data from the controller, it is better to just pass those parameters to the factory's function.
EDIT: I see you managed to do that, and yes - passing $scope.items is the preferred way (and not, for example, passing $scope).
I have my data factory below, which returns a promise
.factory('DataService', function($http, $document, CreatorService) {
promise = null;
jsonData = null;
return {
getJsonDataFromApi: function () {
promise = $http.get('/test');
return promise;
}
}
getJsonData: function () {
return jsonData;
},
setJsonData: function (data) {
jsonData = data;
}
}
My controller makes use of this service as follows
.controller('MainCtrl', function ($scope, $http, $uibModal, DataService, StyleService, CreatorService) {
$scope.dataService = DataService;
$scope.dataService.getJsonDataFromApi().success(function (data) {
$scope.dataService.setJsonData(data['content']);
$scope.jsonData = $scope.dataService.getJsonData();
As you can see, I'm trying to bind $scope.jsonData to the data service jsonData object via $scope.jsonData = $scope.dataService.getJsonData(); but this doesn't seem to work.
If I update the value of $scope.jsonData(), the value returned by $scope.dataService.getJsonData() doesn't change.
For example, if I do
$scope.jsonData = {};
console.log($scope.jsonData);
console.log($scope.dataService.getJsonData());
The output is
{}
Object{...}
I would have expected them to be the same. I can't seem to get my service object to update if I change my controller scope object, nor can I get my controller scope object to update if I make a change to the service object. I want to avoid using watches.
Two way data binding is for binding with your $scope and views. Not for controller and services. So if you really need something like this, then you need to $watch and set the data in service everytime it changes.
I have the following controller:
myApp.controller('myCtrl', ['$scope', '$rootScope', '$location', 'myService',
function($scope, $rootScope, $location, myService) {
$scope.myArray = [];
$scope.myFunction = function() {
if (something) {
setTimeout(function(){
$scope.myFunction();
},500);
} else {
var itm = $rootScope.var;
for (var i in itm) {
if (itm.hasOwnProperty(i)) {
$scope.myArray.push(itm[i].value);
}
}
// first console.log
console.log($scope.myArray);
}
}
$scope.myFunction();
// second console.log
console.log($scope.myArray);
}
]);
In the example above the second console.log gets printed before the first one. Why is this the case? Is there a way to make the controller wait for the function to be executed/returned and only after that proceed to execute the rest of the code?
Without seeing how everything is being implemented. This is the best I can help you with. If you want a controller to do something only if a promise is successful you can wrap your code around the request. In the plunkr I have written a sample $http service that has a fake request to myFunction that uses $q.
I would suggest using a factory to share data between controller instead of $rootScope. $rootScope is hard to manage throughout big SPA's. The Plunkr has commented options you can mess with to change between $rootScope and using a Factory.
Service below
app.service('Service', Service);
function Service($q, $rootScope, Factory) {
var deferred = $q.defer();
this.myFunction = function(){
//Using factory to persit data instead of $rootScope
//var itm = Factory.myArray;
var itm = $rootScope.var;
var array = [];
//Item isnt set return error
if(itm === undefined || itm === null) deferred.reject("$rootScope.var is not set")
//Changed this a bit didnt know what $rootScope.var actually was
for (var i in itm) {
array.push(itm[i]);
}
deferred.resolve(array);
return deferred.promise;
}
return this;
}
The first thing the controller does is initializes a request to Service.myFunction() and waits for a success or error callback. After the success you can process and do anything you'd like with the data returned from the promise. If there is an error you can handle it as you see fit.
app.controller('controller', controller);
function controller(Service, $rootScope) {
/* jshint validthis: true */
var vm = this;
vm.myArray = [];
vm.request = "";
//Un-Comment this to return success or error
$rootScope.var = [1,2,3,4,5,6];
//This is a fake http request
Service.myFunction().then(
//if the promise was resolved or $http was a success
//initilize the controller
function(data) {
vm.myArray = (data)
},
//if the promise was resolved or $http was a success
//initilize the controller
function(err) {
vm.request = (err)
})
}
Plunkr
I am building an app to track movies and their info, I am new to Angular, and I cant not really sure how to pass a variable to this service.
I want the url to be a variable instead of hardcoded. whats the best way to do it?
tmdb.service('tmdbService', function($http, $q){
var deferred = $q.defer();
$http.get('https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh').then(function(data){
deferred.resolve(data);
});
this.getMovies = function(){
return deferred.promise;
}
});
tmdb.controller("tmdbController", function($scope, tmdbService){
var promise = tmdbService.getMovies();
promise.then(function(data){
$scope.movies = data;
// console.log($scope.movies);
})
});
There is no need (in this case) to use $q.defer() because $http already returns a promise. So, your service code can be simplified to:
tmdb.service('tmdbService', function($http){
this.getMovies = function(){
return $http.get('https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh');
}
});
Then, if you want to send a parameter you can do this:
tmdb.service('tmdbService', function($http){
this.getMovies = function(movieId){
return $http.get('https://api.themoviedb.org/' + movieId + '/movie/popular?api_key=jkhkjhkjhkjh');
}
});
In your controller, you can now send in the movieId:
tmdb.controller("tmdbController", function($scope, tmdbService){
tmdbService.getMovies(3).then(function(response){
$scope.movies = response.data;
// console.log($scope.movies);
})
});
I usually do this in the following way which I feel is neat and more readable:
tmdb.service('tmdbService', [function($http) {
return { //simple mapping of functions which are declared later
fn1: fn1,
fn2: fn3
}
function f1(param) { //param can be the url in your case
//fn code example
return $http.post(param).success(function(response) {
return response.data;
})
}
function f2(param) {
}
}]
And in your controller, using the service:
tmdb.controller('tmdbController', ['$scope', 'tmdbService', function($scope, tmdbService) {
tmdbService.f1(url).then(function(data) {
//handle the data here
})
}])
There are several ways you can go about achieving this goal. In my opinion there is really no right/wrong way; what is right is absolutely dependent on your need and this may change as you application grows.
Especially for large applications, you can define a module to manage urls and inject this module into your index application.
Another way is to define a service to manage your urls. In this case you also have to inject this service into any other services/controllers etc where you may need it.
The disadvantage to this is that this service is only available to the angular module its defined within or at most must be accessed via that module.
So using the service style here is how it may be implemented.
tmdb.service('urlService', function () {
this.urls = {
url1: 'https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh',
url2: 'anotherUrl'
};
});
tmdb.service('tmdbService', ['$http', '$q', 'urlService', function ($http, $q, urlService) {
var deferred = $q.defer();
$http.get(urlService.url.url1).then(function (data) {
deferred.resolve(data);
});
this.getMovies = function () {
return deferred.promise;
}
}]);
Again there is no absolute right/wrong way; it depends.
I hope you find this helpful.
Cheers!
I am having a problem getting data from a service populated into my view. I have a service defined as such
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.nuke = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.nukes = data;
});
return nukeService.nukes;
};
return nukeService;
});
and my controller
function NavigationCtrl($scope, $http, nukeService){
/*$http.get('nukes/nukes.json').success(function(data) {
$scope.nukes = data;
});*/
$scope.nukes = nukeService.getNukes();
}
If I use the $http.get from the controller the data populates fine, however, if I try to call the data from the service, I get nothing. I understand that the query is asynchronous but I am having a hard time understanding how to populate the $scope variable once the data is returned. I could use $rootscope to broadcast an event and listen for it in the controller but this does not seem like the correct way to accomplish this. I would really appreciate any advice on how to do this the correct way.
I think this should solve your problem
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.data = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.data.nukes = data;
});
return nukeService.data;
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
$scope.data = nukeService.getNukes();
//then refer to nukes list as `data.nukes`
}
This is a problem with object reference.
when you calls nukeService.getNukes() you are getting a reference to a object a then your variable $scope.nukes refers that memory location.
After the remote server call when you set nukeService.nukes = data; you are not changing the object a instead you are changing nukeService.nukes from referencing object a to object b. But your $scope.nukes does not know about this reassignment and it still points to object a.
My solution in this case is to pass a object a with property data and then only change the data property instead of changing reference to a
This should be as follows. As mentioned by NickWiggill's comment, undefined will be assigned to nukeService.data if we do not return promise.
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
return $http.get('nukes/nukes.json');
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
nukeService.getNukes().then(function(response){
$scope.data = response.data;
});
}
What I do is that I expose the data straight from the service, and have a method which initializes this data. What is wrong with this?
Service:
app.factory('nukeService', function($scope, $http) {
var data = {};
data.nukes = [];
//Gets the list of nuclear weapons
var getNukes = function() {
$http.get('nukes/nukes.json').success(function(data) {
data.nukes = data;
});
};
// Fill the list with actual nukes, async why not.
getNukes();
return {
data : data
// expose more functions or data if you want
};
});
Controller:
function NavigationCtrl($scope, nukeService){
$scope.data = nukeService.data;
//then refer to nukes list as `$scope.data.nukes`
}