AngularJS How To Achieve Polymorphism / Dependency Injection (Best Practices) - javascript

This is a design pattern related question. I am not looking for an answer as to how to achieve the following, but rather the most widely accepted and way to achieve polymorphism in a service.
Suppose I had a service called getData. It needs to get some data, whether it be from a database, text file, or something hardcoded, and output it depending on what the settings are on the $scope in the controller. In this example below, suppose getData depends on the dataSource.
angular.module('testApp').controller('testController'), [$scope, myAwesomeService, function ($scope, myAwesomeService){
$scope.dataSource = 'database'; //defines the source of the data
$scope.getData = function() {
//use myAwesomeService, get the data and output
if($scope.dataSource ==='database') {
return //do it the first way
}
else if($scope.dataSource ==='text') {
return //do it the second way
}
else if($scope.dataSource ==='csvfile') {
return //do it the third way
}
else if($scope.dataSource ==='form') {
return //do it the fourth way
}
}
}]);
Questions:
How would you achieve this generally in Javascript? I am not sure about the best practices around achieving polymorphism in Javascript. I am used to using interfaces and dealing with the situation above by using dependency injection and passing in objects that adhere to the same interface, and call a common method, from the controller. Usually some other "class" would take care of selecting which object to instantiate and pass in, and therefore make the controller agnostic to concrete details as to "how it is done".
How would one go about doing this in AngularJS?
How would the pattern typically look? Can you give a "textbook" Angular way of achieving polymorphism?

I wanted to comment, but I realized it might be too long, so I'm going to post an answer.
If we are talking about ES5, polymorphism & inheritance can be achieved through prototyping.
For example:
function Auto(name,year){
this.year=year;
this.name=name;
}
Auto.prototype.showYear = function(){
console.log(this.year);
}
function Car(name,year, model){
Auto.call(this,name,year);
this.model=model;
}
Car.prototype = Object.create(Auto.prototype);
//usage
var car = new Car('BMW',2015,'320d');
car.showYear(); // will output 2015
In ES6 this can be done using class functions. You can read more about this, HERE ( it's gonna be very nice :D )
Below you'll find some code that might answer your question. Hope this is what you're looking for:
function BaseService(){
this.dataSource='database';
}
BaseService.prototype.getData = function(){
console.log('base: get data');
}
function TextService(){
this.dataSource='text';
}
TextService.prototype = new BaseService();
TextService.prototype.getData = function(){
console.log('child text: get data');
}
function CsvService(){
this.dataSource='csv';
}
CsvService.prototype = new BaseService();
CsvService.prototype.getData = function(){
console.log('child csv: get data');
}
function FormService(){
this.dataSource='form';
}
FormService.prototype = new BaseService();
FormService.prototype.getData = function(){
console.log('child form: get data');
}
angular.module('myApp').factory('awesomeService', function(){
var service={};
service.getData = function(dataSource){
var sourceService;
if(dataSource='database'){
sourceService= new BaseService();
}
if(dataSource=='text'){
sourceService=new TextService();
}
if(dataSource=='csv'){
sourceService = new CsvService();
}
if(dataSource=='form'){
sourceService= new FormService();
}
return sourceService.getData(); // i'm going to assume getData returns a promise
}
return service;
});
angular.module('myApp').controller('myController', function($scope,awesomeService){
var myDataSource='database';
$scope.getData = function(){
awesomeService.getData(myDataSource).then(function(data){
$scope.result=data;
});
}
});

Related

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

Knockout subscribe/event type system without observable?

I want to make use of the subscribe() function of knockout js to manually trigger an event at a certain point.
I could make an observable() and everytime put a GUID in there to trigger the scubscribe.
Is there a cleaner way within Knockout js to have a typical event-like structure?
Edit
Ok, apparently I can use observable.valueHasMutated() - might already a a bit cleaner that using a GUID.
Example
This is the behaviour that I'm looking for:
function Car()
{
var self = this;
self.onOpenDoor = ko.observable();
self.openDoor = function()
{
// using an observable / valueHasMutated for this feels a bit hacky
// is there an other way to use the underlying subscribe() system?
self.onOpenDoor.valueHasMutated();
}
}
var car = new Car();
// multiple subscribers
car.onOpenDoor.subscribe(function()
{
console.log('Do something');
})
car.o**nOpenDoor.subscribe(function()
{
console.log('Do something else');
})
car.openDoor();
I am aware this is not the default 'knockout' way to do stuff - that is not what this question is about.
Update
After #RoyJ's reference to Niemeyer's blog I went for this solution:
function Car()
{
var self = this;
self.onOpenDoor = new ko.subscribable();
self.openDoor = function()
{
self.onOpenDoor.notifySubscribers();
}
}
Update If you're just looking for clarity, you can use notifySubscribers instead of valueHasMutated. You might want to take a look at the base type of observables, ko.subscribable.
I would do it like this:
var vm = {
///...definitions...
openCount: ko.observable(0),
openDoor: function () {
vm.openCount(vm.openCount()+1);
}
};
vm.openCount.subscribe(function () {
///...do something
});
vm.openCount.subscribe(function () {
///...do something else
});
ko.applyBindings(vm);
Demo http://jsfiddle.net/uoqdfhdb/2/

Moving logic to a service in Angular

I've ended up with a lot of logic in my controller which I realise is not good. Therefore I would like to move this to a service.
At the moment the controller accepts a url which will either be from YouTube or Vimeo. It detects whether the string "youtube" or "vimeo" is present in the url and then does what it needs to do accordingly. Here's part of the "logic" that currently resides in the controller:
if url.indexOf("youtube") > -1 {
variable_1 = "Something";
variable_2 = "Something";
//do some more stuff
}
else {
variable_1 = "Something";
variable_2 = "Something";
//do some more stuff
}
$scope.task.items.push("I need to add things to this array too");
A Service is the way to go but my first question is a service or a factory?
This is what I'm working on but I'm not sure how I would pass the variables that exist in the controller (variable_1 and variable_2) back to the controller when the service has completed.
myApp.service('urlService', function() {
this.detectProvider = function() {
if url.indexOf("youtube") > -1 {
}
else {
}
//how can I push things to the $scope array here?
};
});
In your service
myApp.service('urlService', function() {
this.detectProvider = function(url) {
arrayOfMyVars = new Array();
if url.indexOf("youtube") > -1 {
arrayOfMyVars.push("Something");
arrayOfMyVars.push("SomethingElse");
}
else {
arrayOfMyVars.push("Something");
arrayOfMyVars.push("SomethingElse");
}
//how can I push things to the $scope array here?
return arrayOfMyVars;
};
});
in your controller
var res = urlService.detectProvider(url);
variable_1=res[0];
variable_2=res[1];
$scope.task.items.push('the thing you need to push'); // maybe res[2] and in your service you had another arrayOfMyVars.push('the thing you need to push')...
Don't forget to import your service into your controller ;)
A Service is the way to go but my first question is a service or a
factory?
Simply speaking it does not matter. From personal point of view just use what suits you best. A service will create a new instance of the function, a factory will simply execute the function and do not create a new instance of it.
About your second question: Simply return variable_1 and variable_2 in your services method and assign them to your $scope.
myApp.service('urlService', function() {
this.detectProvider = function(url) {
if url.indexOf("youtube") > -1 {
...
return [variable_1, variable_2];
}
else {
...
return [variable_1, variable_2];
}
};
});
Service or Factory both are singleton instances. At the end u will get object only.
In service you will create using functtion constructor
In Factory we can construct it using object literrals

Get calling arguments for getter in javascript

Given a javascript object like this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
// access caller name here, so I can return cool/neat stuff
}
});
I want to be able to get children of myThing.gen, but know what is being asked for in the getter.
for example:
var coolThing = myThing.gen.oh.cool;
var neatThing = myThing.gen.oh.neat;
I want the "oh.cool" or "oh.neat" part in getter, so I can make decisions based on this, and return something specific to it. I am ok with solution not working in IE, or old browsers, as it is primarily for node.
The actual purpose of this is so that I can request myThing.gen.oh.neat and have the myThing.gen getter resolve to require('./oh/neat.js') and return it.
Since require cache's, this is an efficient way to dynamically load modular functionality, and have a tidy interface (rather than just dynamically building the require where needed) without having to know the structure ahead of time.
If there is no introspection-of-name function that can get this for me, I could just do something less elegant, like this:
myThing.gen = function(name){
return require('./' + name.replace('.', '/') + '.js');
}
and do this:
neatThing = myThing.gen('oh.neat');
I don't like this syntax as much, though. I looked at chai's dynamic expect(var).to.not.be.empty stuff, but couldn't figure out how to do it completely dynamically. Maybe there is not a way.
without actually solving the problem of dynamically discovering the caller, I can do this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
return {
'oh':{
'cool': require('./oh/cool.js'),
'neat': require('./oh/neat.js')
}
};
}
});
Is there a way to do this dynamically?
You can't see what the property gen will be used for in the future, so you would need to return an object with properties that react to what the object is used for when it actually happens:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
var no = {};
Object.defineProperty(no, 'cool', {
get: function(){ alert('cool'); }
});
Object.defineProperty(no, 'neat', {
get: function(){ alert('neat'); }
});
return { oh: no };
}
});
Demo: http://jsfiddle.net/UjpGZ/1/

Standard way of returning backbone collection in requirejs

I've seen different examples in different articles about how to return a Backbone collection (or View, for that matter) from a RequireJS define. For example:
define(['models/person'], function( person ) {
var personCollection = Backbone.Collection.extend({
model: person,
url: "api/person"
});
// do this?
return new personCollection();
// or this?
//return personCollection;
});
Is there a memory advantage to either approach? Is there standard design pattern that dictates which should be used?
The same question would apply to views, as I've seen them done both ways too.
I would do the second way, because then you would receive reference to "blueprint" not the object itself. In most cases one should want to initialize object creation itself.
Also, even if you want only single instance of collection created I would advise using factory method like this:
define(['models/person'], function( person ) {
var personCollection = Backbone.Collection.extend({...}),
singleCollection,
export = {
getInstance = function (options) {
if (!singleCollection) {
singleCollection = new personCollection(options);
}
return singleCollection;
}
}
return export;
});
And then you could call it like this:
require('models/person', function (person) {
var personCollection = person.getInstance(options);
}

Categories