Getters and Setters in AngularJS - javascript

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]);

Related

Pass a variable to a service with AngularJS?

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!

Using http requests, promises, ng-options, and factories or services together

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);
}]);

Factory has two methods, one is $http method with $promise - how to reference the other method from within the first one's success function?

In one of my factories I need to set a variable when data is fetched (through $http) so I can access it in my controller (the intention is to display a spinner image until the data is loaded).
.factory('LoadData', function LoadData($http, $q){
return {
getMyData: function(){
return $http(
// implementation of the call
).then(
function(response){
var myData = response.data;
// this should be reference to the other method (getLoadStatus)
// where I want to change its value to "true"
// this doesn't work - "this" or "self" don't work either because we're in another function
LoadData.getLoadStatus.status = true;
}
);
},
// by calling the below method from my controller,
// I want to mark the completion of data fetching
getLoadStatus = function(){
var status = null;
return status;
}
}
}
I hope you got the idea - how could this be accomplished? Also, I'm open to any suggestions which are aimed towards a better approach (I want to pick up best practice whenever possible).
Status is essentially a private variable; use it as:
.factory('LoadData', function LoadData($http, $q){
var status = null; // ESSENTIALLY PRIVATE TO THE SERVICE
return {
getMyData: function(){
return $http(...).then(function(response){
...
status = true;
});
},
getLoadStatus = function(){
return status;
}
};
})
There are several ways.
Here's one which I prefer to use:
.factory('LoadData', function LoadData($http, $q){
var status = false;
var service = {
getMyData: getMyData,
status: status
};
return service;
function getMyData() {
return $http(
// implementation of the call
).then(
function(response){
var myData = response.data;
status = true;
}
);
}
}
This provides good encapsulation of your methods and gives you a clean interface to export. No need for the getter method if you don't want it.
Inspiration via John Papa's Angular style guide (found here).
You could simply store variable flag in closure:
.factory('LoadData', function LoadData($http, $q) {
var status = false;
return {
getMyData: function() {
status = false;
return $http(/* implementation of the call */).then(function(response) {
status = true;
return response.data;
});
},
getLoadStatus: function() {
return status;
}
}
});
Also if getMyData loads fresh data every time, it's important to reset status to false before each request.
I actually decide to use a promise in my controller after calling this service and when data is returned, I am simply making the status true. It seems that is best practice to have a promise returned when calling a service so I should be good.

AngularJS automatically updating controller data by service

I'm having some basic problems with angular at the moment. I just wrote a service that reads the temperature of an external device in an interval of five seconds. The service saves the new temperature into a variable and exposes it via a return statement. This looks kind of this (simplified code):
angular.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = 0,
requestTemp = function() {//some logic here},
onResponseTemp = function (temp) {
m_temp = temp;
},
//some other private functions and vars ...
foo = bar;
//request new temperture every 5s, calls onResponseTemp after new data got received
$interval(requestTemp, 5000);
return {
getTemp = function(){return m_temp;}
}
}]);
I use a controller to fetch the data from the service like this:
angular.controller("tempCtrl", ["$scope", "tempService", function ($scope, tempService) {
$scope.temp = tempService.getTemp();
}]);
In my view I access it like this:
<div ng-controller="tempCtrl">
<p>{{temp}}</p>
</div>
But I only get 0 and the value never changes. I have tried to implement a custom Pub/Sub pattern so that on a new temperature my service fires an event that my controller is waiting for to update the temperature on the scope. This approach works just fine but I'm not sure if this is the way to go as angular brings data-binding and I thought something this easy had to work by itself ;)
Help is really appreciated.
Please see here http://jsbin.com/wesucefofuyo/1/edit
var app = angular.module('app',[]);
app.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = {
temp:0,
time:null
};
var requestTemp = function() {
m_temp.temp++;
m_temp.time = new Date();
};
var startTemp = function() {
$interval(requestTemp, 3000);
};
return {
startTemp :startTemp,
m_temp:m_temp
};
}]);
app.controller('fCtrl', function($scope,tempService){
$scope.temp = tempService;
$scope.temp.startTemp();
});
You are returning a primitive from your service, if you want to update an primative you need to reftech it. You should return an object, as on object is passed by reference, you get the actual values in your controller.
do this in your service:
return m_temp;
And this in your controller:
$scope.temp = tempService;
and your view will update as soon as the service gets updated.
Does this help you?
i think you should use $interval in controller ot in service
$interval(tempService.getTemp(), 5000);

Getting a Filter to handle a Promised Service

I have already started to rework this code to operate synchronously, but out of curiosity and a desire to support both means, I need some help understanding how to get a filter to jive with a promise. As some other posts mention a filter seems to just resolve to {} from a promise.
Basic Pattern
Here's a breakdown:
Define a service in the module that returns a promise instead of an object
module.factory('promisedSvc', ['$http', function($http) {
var httpPromise = null,
servicePromise = null,
service = {},
dataSet = {};
var httpPromise = $http.get('somedata.json').success(function(data) {
dataSet = data;
});
servicePromise = httpPromise.then(function(){
service.getData = function(key) {
return dataSet[key];
};
service.addData = function(key, value) {
dataSet[key] = value;
};
return service;
});
/*
In actuality I proxied the service methods onto the promise because
I didn't want consumers of the service to have to deal with it being
a promise. There is the caveat of setting properties on a class I
don't own (property collisions), a risk I'm okay taking, but YMMV
Commented out proxies
servicePromise.getData = function(key) {
return this.then(function(svc){
return svc.getData(key);
});
};
servicePromise.addData = function(key, value) {
this.then(function(svc){
svc.addData(key, value);
});
};
*/
return servicePromise;
}]);
Controllers can handle this promisedSvc fine, you just get the promise injected into the controller and then use the then function on the promise to wrap the setting of a $scope property to the function call on the eventual service object: getData(key) or setData(key, value). Alternately you can just treat it as normal if you proxied the functions onto the promise like in the commented out block.
Filters do not seem to inherently handle promises like $scope does. I am looking for a way to get the filter to inject the promisedSvc and be able to call getData(key) without it resolving to {} because the promise has not resolved yet. Below is an example of what does not work:
module.filter('svcData', ['promisedSvc', function(promisedSvc) {
return function(input) {
return promisedSvc.then(function(svc) {
var value = svc.getData(input);
return value;
});
};
}]);
So is there a way to write the filter to be able to resolve the value?
Use Case
That is the simplified pattern of what I am trying to achieve. For those curious, my actual use case is to pre-fetch i18n/l10n resource bundle information so I can localize all the text in my application. The pre-fetch could all be in the Javascript environment (attached to some already loaded global or in a provider), but we also have scenarios with database-stored Resource Bundles so I needed a version of code that can pre-fetch all the information from the server via AJAX.
It's not exactly what I'm looking for, but at least to document a workaround:
It's possible to use a function on the $scope instead of a filter.
module.factory('promisedSvc', ['$http', '$rootScope', function($http, $rootScope) {
var httpPromise = null,
servicePromise = null,
service = {},
dataSet = {};
var httpPromise = $http.get('somedata.json').success(function(data) {
dataSet = data;
});
servicePromise = httpPromise.then(function(){
service.getData = function(key) {
return dataSet[key];
};
service.addData = function(key, value) {
dataSet[key] = value;
};
//Here is the addition to setup the function on the rootScope
$rootScope.svcData = function(key) {
return service.getData(key);
};
return service;
});
return servicePromise;
}]);
And then in a template instead of {{ 'key1' | svcData }} you would use {{ svcData('key1') }}
I tested that if you delay the promises resolution (for example setup a wait in the $http.success) that the impact is the page loads, but the values from the svcData function will only populate into the template once the promises resolve.
Still would be nice to accomplish the same with a filter if possible.

Categories