Pass a variable to a service with AngularJS? - javascript

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!

Related

Accessing $scope data from view to factory in AngularJs

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).

Two Factories. One should know when there is a change in Other

Before i state my question, i may be completely wrong with how i am using factories in my use case. Happy to know better alternatives.
So, I have two Factories.
Factory1 takes care of hitting the server, getting and storing that data.
Factory2 does not make any REST Calls. But stores a set of data that would be useful while making REST calls.
Simple question would be, why not Factory1 store that data instead of Factory2. The motive behind Factory2 is that this data would be used by lot of other factories/controllers. So, to remove redundancy this looks a better option.
For now,
When a REST call is to be made from Factory1, i can simply do Factory2.getData and then use it. But my use-case is bit more complex. I need Factory1 to fire calls whenever there is a change in Factory2.getData(). We would use $scope normally in case of controllers etc. But how to achieve the same thing here ?
Basically, How to do we know in Factory1 when there is a change in Factory2's data ?
Code:
servicesModule.factory('Factory2', function() {
var fObj = {},
filters;
fObj.setFilters = function(fs) {
filters = fs;
};
fObj.getFilters = function() {
return filters;
};
return fObj;
});
servicesModule.factory('Factory1', ['$http', '$q', 'Factory2', function($http, $q, Factory2) {
var fObj = {},
data;
fObj.fetchData = function(params) {
var filters = Factory2.getFilters();
// REST call here and set data to result
}
fObj.getData = function(){
return data;
}
}]);
So, a bit of digging pointed in the direction of $rootScope.
servicesModule.factory('Factory2', ['$rootScope', function($rootScope) {
var fObj = {},
filters;
fObj.setFilters = function(fs) {
filters = fs;
$rootScope.$emit("FiltersUpdated", fs);
};
fObj.getFilters = function() {
return filters;
};
return fObj;
}]);
servicesModule.factory('Factory1', ['$http', '$q', 'Factory2', '$rootScope', function($http, $q, Factory2, $rootScope) {
var fObj = {},
data;
$rootScope.$on('FiltersUpdated', function(event, data) {
fObj.fetchData();
})
fObj.fetchData = function(params) {
var filters = Factory2.getFilters();
// REST call here and set data to result
}
fObj.getData = function(){
return data;
}
}]);
I would still love to hear others' opinions.

Getting results twice when calling service with controller

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

Getters and Setters in AngularJS

confirm("Ohhh, hello there, is it Ok to click Cancel?");
I think that this is, basically, a question about CRUD on Angular. I'm kind of confused about getters and setters, mainly because Angular do almost all the job in getting and setting things because of its two way data binding. I want to know what's the best scalable way to create getters and setters so I wont need to modify my functions in the future.
On the first Arrangement, I'm trying to be as simple as I can be, but I feel uncomfortable in getting and getting to set.
Arrangement 01:
$scope.getData = function(){
$http.get(url + '/data')
.then( function (res) {
return res.data; } );
};
$scope.setData = function () {
$scope.data = $scope.getData();
};
$scope.insertData = function (data) {
$http.post(url + '/data', { data: data})
.then( function (res) {
// nothing here. } );
};
On this second Arrangement, however, I'm trying to go directly where I need to. When I fetch data from the server, I'm automagicaly setting my $scope.data to the retrieved data;
Arrangement 02:
$scope.getData = function () {
$http.get(url + '/data')
.then( function (res) {
$scope.data = res.data;
});
};
$scope.insertData = function (data) {
$http.post( url + '/data', { data: data })
.then( function (res) {
$scope.getData(); //To update.
//OR $scope.data.push(res.data);
});
};
Looking further, I've found this on the Angular Docs, but what's the point in using a getter/setter if Angular already do it? Looking into other technologies, it's hard to compare, because Angular has auto-get.
I don't even know how to formulate this question. But, basically, I want to know how could my getters and setters harm my future application and if there's a good way and why to create getters and setters in Angular.
Thanks for any advice.
You good practice is to wrap your logic into Service. You have to know that in Angular, all services are Singleton, there is only a single instance of a Service.
I've made a simple example, by using $q.defer() which is the promise manager from the deferred API.
$q.defer() get 2 methods :
resolve(value) : which resolve our associated promise, by giving her the final value
reject(reason) : which resolve an promise error.
Controller
(function(){
function Controller($scope, $q, Service) {
//Use promise manager
var defer = $q.defer();
///Create our promise
var promise = defer.promise;
$scope.data = [];
var newData = [
{
name:'john',
age: 25
},
{
name: 'toto',
age: 13
}
];
Service.get().then(function(data){
//Retrieve our data
$scope.data = data;
//Set new data to our factory
Service.set(newData);
//Retrieve new data
Service.get().then(function(data){
//Resolve new data
defer.resolve(data);
});
});
//Retrieve new dataset
promise.then(function(data){
$scope.data = data;
})
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service($q){
var data = [0,1,2,3,4];
function set(value){
data = value;
}
function get(){
return $q(function(resolve){
//Simulate latency
setTimeout(function(){
//Resolve our data
resolve(data);
}, 1000);
});
}
return {
get: get,
set: set
};
}
angular
.module('app')
.factory('Service', Service);
})();
HTML
<body ng-app='app' ng-controller="ctrl">
<pre>{{data}}</pre>
</body>
So, you can set some data by using the service, and retrieve it when you want. Don't forget that service is singleton.
You can see the Working Plunker
In JavaScript you typcially don't use getters and setters like in OOP languages, especially because you do not have a notion of privateness (so anyone can access your fields). ES5 has getters and setters, but it also adds this missing capabilities of hiding implementation details. In case you want getters and setters for additional logic in your AngularJS app, you could simply define additional fields which are updated using $watch.
Furthermore you solution with sending an HTTP request on every change is a it of an overhead if you do this per field. What you instead to is writing directly to fields.
While e.g. WPF/C# requires you to define setters to raise OnPropertyChanged, you don't need this in AngularJS. Everything that you write in AngularJS will automatically trigger a so-called $digest cycle, where it checks for changes that have been made. It will then automagically update your user interface, give that you use template bindings or ng-model directives.
If you think like pure Javascript, is basic the same logic, what angular does is create modules for you to use the best practice, so it is easy to use them.
function DataService($http) {
this.get = function() {
return $http.get(...);
}
this.create = function(newData) {
return $http.post(...);
}
..
}
and using angular, like Ali Gajani sayd, you basically can do this,
angular.module('myApp').service('DataService', ['$http', DataService]);
or with a factory style
function DataService($http) {
var myPrivateVariable = "something";
function get() {
return $http.get(...);
}
...
// expose them public
return {
get: get
};
}
angular.module('myApp').factory('DataService', ['$http', DataService]);

Share data between controllers in AngularJS

I use the following factory to fetch data from API and store it to local variable called apiData.
app.factory('MyFactory', function($resource, $q) {
var apiData = [];
var service = {};
var resource = $resource('http://example.com/api/sampledata/');
service.getApiData=function() {
var itemsDefer=$q.defer();
if(apiData.length >0) {
itemsDefer.resolve(apiData);
}
else
{
resource.query({},function(data) {
apiData=data;
itemsDefer.resolve(data)
});
}
return itemsDefer.promise;
};
service.add=function(newItem){
if(apiData.length > 0){
apiData.push(newItem);
}
};
service.remove=function(itemToRemove){
if(apiData.length > 0){
apiData.splice(apiData.indexOf(itemToRemove), 1);
}
};
return service;
});
I inject the apiData into my controllers the following way:
$routeProvider
.when('/myview', {
templateUrl: 'views/myview.html',
controller: 'MyController',
resolve: {
queryset: function(MyFactory){
return MyFactory.getApiData();
}
}
})
app.controller('MyController', ['$scope', 'queryset',
function ($scope, queryset) {
$scope.queryset = queryset;
}
]);
Is it a good way to share a promise between different controllers or I better to use local storage or even cacheFactory?
How can I rewrite MyFactory add() and remove() methods so that I can keep my shared variable in a current state (no API update/create/delete calls needed)?
Thank you!
You should use $resource to get $promise eg: resource.query().$promise instead of $q.defer() for cleaner code. Otherwise you are good to go. You could use $cacheFactory, but you can also use local var.
Isn't your shared variable in a current state with current code?
I recommend you take a look at ui-router and its nested and abstract views, where resolved values are available to child controllers.

Categories