AngularJS + OOP is kinda sexy feature to use
Hi, I'm successfully using OOP with AngularJs for some time already (first started with angularjs with oop inheritance in action), the provided approach allows you define your classes as angular services, which you can later extend or inherit from like that:
Application.factory('AbstractObject', [function () {
var AbstractObject = Class.extend({
virtualMethod: function() {
alert("Hello world");
},
abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable
throw new Error("Pure abstract call");
}
});
return AbstractObject; // You return class definition instead of it's instance
}]);
Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) {
var DerivedObject = AbstractObject.extend({
virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world`
alert("Hey!");
this._super();
},
abstractMethod: function() {
alert("Now I'm not abstract");
}
});
return DerivedObject;
}]);
Plunker: http://plnkr.co/edit/rAtVGAsNYggBhNADMeoT
using the described approach gives you the ability to define classes that beautifully integrate into angular infrastructure. You get all sort of nifty features from two worlds - OOP and AngularJs. Dependency injection is free for your classes, and it makes your classes simple, allows putting a lot of boilerplate controller code into some base class that can be later reused.
However
AngularJs infrastructure blocks previously described approach from spreading it's wings on all 100%. The problem occurs when you try to define recursive class definitions (i.e. recursive aggregation), say you have two class definitions like Blog and Tag
Application.factory('Blog', ['Tag', function (Tag) {
var Blog = Class.extend({
tags: function() {
return this.tags;
}
});
return Blog;
}]);
Application.factory('Tag', ['Blog', function (Blog) {
var Tag = Class.extend({
Blogs: function() {
return this.blogs;
}
});
return Tag;
}]);
It won't work because both Blog and Tag are self-referencing themselves causing circular dependency.
P.S
The last thing, I have found kinda ugly solution that solves my problem in my specific case but doesn't work in general and as I said, it isn't pretty:
Application.factory('BlogNamespace', [function () {
var Blog = Class.extend({
tags: function() {
return this.tags;
}
});
var Tag = Class.extend({
Blogs: function() {
return this.blogs;
}
});
return {
Tag: Tag,
Blog: Blog
};
}]);
Question
The above fix won't work because namespaces may also be a subject of circular dependency. This means that it isn't solution to described problem but rather one level deeper problem now.
Any suggestions on how it is possible to solve described problem in general case?
A circular dependency is always the sign of mixing of concerns, which is a really bad thing. Miško Hevery, one of the authors of AngularJS, explains a nice solution on his awesome blog. In short, you probably have a third service hidden somewhere, which is the only part of your code really needed by the two others.
I'm answering my own question just because I've found a technical way of resolving the issue that I have originally posted about. But before that, I strongly encourage you to use Blackhole's suggestion since it allows solving a broader set of problems which are usually caused by bad architecture. Please prefer using his approach first, and return to current one in case that you know what you are doing.
So here goes:
You can use $injector service and inject required definitions at run-time, which is legal from technical point of view, but again according to this post (hard to imagine that it is written in 2008), this is like a black magic, do that and it will strike you back:
Application.factory('Blog', ['$injector', function ($injector) {
var Tag = $injector.get('Tag'); // Here is your tag
...
}]);
Application.factory('Tag', ['Blog', function (Blog) {
...
}]);
Edit
It turned out that current approach is an example of Service Locator pattern, which is IoC Antipattern.
LAST RESORT: NOT ENCOURAGED
In my case the best way to get around a circular-dependency-problem like this in angular, is to trigger function-calls via $rootScope-broadcasts. The other service can then listen to this broadcast and react with the desired function-call. It may not be the most elegant solution but in some cases where the interaction between the services is mainly one-directional anyways, it may be a reasonable alternative. (note that this also allows return-values to be passed back to the broadcasting function via callbacks only)
A pseudo-example of this would be:
angular.module('myApp').factory('service1', ["$rootScope",
function($rootScope) {
function func1() {
// do something
}
$rootScope.$broadcast("callFunc2"); // calls func2 from service 1
return {
func1: func1
}
}
]);
angular.module('myApp').factory('service2', ["service1", "$rootScope",
function(service1, $rootScope) {
function func2() {
// do something
}
service1.func1(); // calls func1 from service 2
$rootScope.on("callFunc2", func2);
}
]);
Related
We are migrating our site from old angularjs to using Vuejs.
Step one is to modify our services used throughout the site which all rely heavily on ngResource and convert them into vanilla js code that can be called by Vue.
The challenge is that in addition to making API calls using ngResource they also extending the returning object via prototype.
While using a the module pattern in regular javascript I can mimick the API behaviour of the ngResource service. But I am not clear how to set this up so that it can also support the prototype extensions that are being applied to the returning object (whether a single object or an array).
For example one of our current services might look like this
"use strict";
angular.module("myApp")
.factory("PortfolioService",
[
"$resource", "$rootScope", "$http",
function($resource,
$rootScope,
$http) {
var Portfolio = $resource("services/portfolios/:Uid",
{
'_': function() { return Date.now() }
}, {
'query': {
method: "GET",
url: "services/portfolios/",
transformResponse: $http.defaults.transformResponse.concat([
function (data) { return data.Data; }
])
}
});
Portfolio.prototype.getPicUrl= function() {
return this.ImgBasePath + this.ImgUrl;
};
return Portfolio;
}
]);
Note: that it mames a service call called query but also extends the returning object with a new function called getPicUrl.
I have created a JS equivalent that looks like this
const vPortfolioService = (() => {
var baseapipath = "http://localhost:8080/services/";
var Portfolio = {
query: function() {
return axios.get(baseapipath + "portfolios/");
}
};
Portfolio.prototype.getPicUrl= function () {
return this.ImgBasePath + this.ImgUrl;
}
return Portfolio;
})();
The service part works fine but I dont know how to do what ngResource seems to do which is to return a resource from the API which includes the prototype extensions.
Any advice would be appreciated.
Thanks
As I mentioned in my replies to #Igor Moraru, depending on how much of your code base you're replacing, and how much of that existing code base made use of the full capabilities of ngResource, this is not a trivial thing to do. But just focusing on the specific example in your question, you need to understand some vanilla JS a bit more first.
Why does the Portfolio object have a prototype property when it's returned from $resource(), but not when it's created via your object literal? Easy: The object returned by $resource() is a function, which in turn also means it's a class, and those have a prototype property automatically.
In JavaScript, regular functions and classes are the same thing. The only difference is intent. In this case, the function returned by $resource() is intended to be used as a class, and it's easy to replicate certain aspects of that class such as the static query method and the non-static (i.e., on the prototype) getPicUrl method:
const vPortfolioService = (() => {
var baseapipath = "http://localhost:8080/services/";
class Portfolio {
constructor(params) {
Object.assign(this, params);
}
static query() {
return axios.get(baseapipath + "portfolios/").then(res => {
// this convert the objects into an array of Portfolio instances
// you probably want to check if the response is valid before doing this...
return res.data.map(e => new this(e));
});
}
getPicUrl() {
return this.ImgBasePath + this.ImgUrl;
}
}
return Portfolio;
})();
But the problem is, this probably isn't enough. If you're migrating/refactoring an entire application, then you have to be certain of every instance in which your application uses ngResource, and based on your question, I'm fairly certain you've used it more than this class would allow.
For example, every class created by $resource also has static methods such as get, post, etc., as well as corresponding instance methods such as $get, $post, and so on. In addition, the constructor I've provided for the class is just a very lazy stop-gap to allow you to create an instance with arbitrary properties, such as the properties referenced by the getPicUrl method.
So I think you have three options:
Continue playing with the above class to fit something closer to what you need, and then edit every place in your application where your code relies on the Portfolio Service so that it now reflects this new, more limited class. This is probably your best option, especially if your application isn't that big and you don't have to worry about someone else's code not working
Analyze the source code for ngResource, rip it out, and modify it so it doesn't need AngularJS to work. Perhaps someone has already done this and made it available as a library? Kind of a long shot I'd guess, but it may work.
Keep AngularJS in your application, alongside Vue, but only use it to grab the essentials like $http and $resource. An example implementation is below, but this is probably the worst option. There's additional overhead by bootstrapping pieces of angular, and tbh it probably needs to bootstrap other pieces I haven't thought of... but it's neat I guess lol:
const vPortfolioService = (() => {
var inj = angular.injector(["ng", "ngResource"]);
var $http = inj.get("$http"), $resource = inj.get("$resource");
var Portfolio = $resource("services/portfolios/:Uid",
{
'_': function () { return Date.now() }
}, {
'query': {
method: "GET",
url: "services/portfolios/",
transformResponse: $http.defaults.transformResponse.concat([
function (data) { return data.Data; }
])
}
});
Portfolio.prototype.getPicUrl = function () {
return this.ImgBasePath + this.ImgUrl;
};
return Portfolio;
})();
Object instances does not exposes prototype property. Instead you can access it by using:
object.__proto__ // not recommended, or better
Object.getPrototypeOf(object)
Object.getPrototypeOf() return object's prototype object, which you can use to assign new properties.
Object.getPrototypeOf(Portfolio).getPicUrl= function () {
return this.ImgBasePath + this.ImgUrl;
}
Note: You still can, though, access the prototype of Function() by doing Function.prototype.
UPDATE: Your Portfolio should be a new object, created from the global javascript object, to avoid the issue that #user3781737 has mentioned.
var Portfolio = Object.create({
query: function() {
return axios.get(baseapipath + "portfolios/");
}
});
A few days ago I've found a TypeScript language. According to video reviews it looks pretty promising for me as it brings to JavaScript an appropriate code completion, implicit code documentation and, probably, makes it more type safety.
I'm trying to rewrite a simple AngularJS application in TypeScript. I'd like to specify types of parameters, which are injected to controllers. Let's assume the following JS code:
// service.js
angular.module('app').service('MyService', MyService);
function MyService() {
this.test = function test() { console.log('test'); };
}
// controller.js
angular.module('app').controller('MyController', MyController);
function MyController($scope, MyService) {
$scope.test = function test() { MyService.test(); };
}
I'm trying to achieve something like this:
// controller.ts
angular.module('app').controller('MyController', MyController);
function MyController($scope, MyService:MyService) {
// ...
}
So it'd be easier to write code of the controller with code completion for MyService parameter. However, this code makes a compiler to emit error TS2304: Cannot find name 'MyService'.
I've tried a couple to things and found at least 2 possible solutions. However, non of them seems to be ideal.
1) Creation of an additional interface, which declares all methods of the service. But this approach requires a lot of additional typing (actually, we define the same set of methods with full signature twice and also have to update several things in case of changes). This way is totally okay when the interface is implemented multiple times, but looks redundant for a single AngularJS service.
2) Constructor function can be replaced with a class. This way it is possible to use a name of the class as a type of the parameter. However, this also leads to a couple of problems:
typescript compiler transforms class definitions into closures which are assigned to variables declared with var keyword (at least, dealing with ES5). This requires the AngularJS service declaration to be placed after the class definition at the very bottom of a file. It increases a possibility to forget to do it.
angular.module('app').service('MyService', MyService);
class MyService {
test() { console.log('test'); }
}
// becomes
angular.module('app').service('MyService', MyService);
var MyService = (function () {
function MyService() {
}
MyService.prototype.test = function () { console.log('test'); };
return MyService;
}());
going this way, we have to inject dependencies to a constructor function of the service. As a result, it is necessary to specify an excess this. string each time a dependency is used.
So, are there any other ways to pass a constructor function as a type of a parameter of another function? Thanks.
P.S.: I don't know whether it's necessary or not, but I'm using Sublime Text 3 with an official TypeScipt plugin and gulp-typescript for compilation.
Yes, you can do this. See working CodePen
class MyController
{
static $inject = ['MyService','$scope'];
constructor(private myService:MyService, private $scope:any){
this.$scope.test = this.myService.test;
}
}
class MyService{
test(){
console.log('test');
}
}
I am the team lead of a group of ~8 developers. We look after a large website which is split into many 'components' (a component could be a gallery for example - with libraries aside, these 'components' are standalone). We are in the process of splitting things up and part of that task is creating Gulp code to handle the various stages of processing for each of these components (SCSS processing, concatenation, image optimisation etc).
So, I need to come up with a pattern that the team can follow when creating Gulp code for a new 'component'. I need to keep this as simple as possible as many of the developers are new to Gulp. The general idea I want to get to is that we have a base Gulp 'component', which will have all code required to process a standard 'component', but I expect there will be some 'components' that need special gulp code. So, I would like to be able to extend the base Gulp 'component' compilation code in these cases.
In an attempt to learn and to set up a strong foundation for the team have been doing some reading on best approaches to inheritance in JavaScript. I have come across quite a rift in the way people feel about this. What approaches I have considered and what I've gathered:
There are classes in ES6 which I can use in node.js. These classes are shunned by a lot of the big names in the JavaScript world as well as big names from the past (when using classical style languages) for reasons such as it encourages brittle code. Also, you cant do classic style public and private properties/functions, so I struggle to see any real reason why I should go with this.
If I did go this route, I feel I would end up with something like this (code is untested / probably not correct, i'm just dumping my thoughts):
class Component {
constructor(options) {
},
build() {
},
dev() {
}
test() {
},
// Should be private, but wont be
_processStyles() {
},
_processScripts() {
}
}
Factory functions. We're used to using these with the revealing module pattern, and generally I like them. I also believe that Douglas Crockford is a fan of factory functions, so I feel I'm on good ground with this. Now, if I create public and private methods (by returning an object with references only to my public functions) and then I want to extend 'component', in the new factory I would create an instance of 'component' and then extend that. The problem is that I can't override (or even call) the private functions of my 'component' instance, because they are in a different scope that I have no access to. I did read that one way to get around this is to use an object to create a reference to all of the private methods, but then they're not private anymore, so it defeats the object (no pun intended).
var component = function(options) {
var init = function() {
};
var build = function() {
};
var dev = function() {
};
var test = function() {
};
var _processStyles = function() {
};
var _processScripts = function() {
};
return {
init: init,
build: build,
dev: dev,
test: test
};
};
var specialComponent = function(options) {
// Create instance of component
var cmp = component(options);
// Extend it
cmp.extraFunction = function() {
// This will throw an error as this function is not in scope
_processStyles();
}
// Private functions are available if I need them
var _extraPrivateFunction = function() {
}
return cmp;
}
So, I feel like I've missed something somewhere, like I need someone to point me in the right direction. Am I getting too hung up about private functions (feels like it)? Are there better approaches? How would something like this, which seems to lend itself to classical inheritance be best tackled in a DRY (don't repeat yourself) manner?
Thanks in advance,
Alex.
I have an angularjs app, which has several controllers with several viewes. When I started coding the app there were few methods(of similar kind), which were used in more than places, initially it looked OK to me to put those methods where ever needed (two or three controllers). But as my application grew I started noticing the code redundancy issue, as the method was required at more places.
Definitely this was my lack of experience which lead to this issue.
Now I decided to remove this redundant code from my app. I found the following options:
Service way: Remove the redundant code and put it in a new service and include the service all the places where I need the functionality.
External JS: Putting the redundant code in an external js file and just call the required method.
Leave the code as it is.
But here I want to mention that the redundant code I am talking is not very tight coupled and can be added and removed any where with a very little effort.
Now, what I want to know is from the above options, which one should I choose? OR are there much better options available for this issue(may be provided by angularJS itself).
Edit Basic examples of code:as I said
$scope.showSomething = function (show, ele) {
//show or hide the ele (element) based on value of show
}
You are using angular so surely you want to make your app structured according to MVC. what kind of MVC pattern your app will follow if your code is put anywhere in a js file like anything. I would recommend putting your code inside a service. that's the angular way of reducing redundancy. Also you have another option to set the function to the $rootscope here
I'm not sure about your case, but I had similar situation: I had a number of functions which provide validation functionality. I created service:
'use strict';
angular.module('myApp')
.factory('UtilService', ['Env', 'IBANChecker', function(Env, IBANChecker) {
var validateCallbacks = {
checkAddress: function (address) {
return address.action != 'delete';
},
validateIBAN: function (iban) {
return !iban || IBANChecker.isValid(iban);
},
.....
validateYCode: function(id) {
return YCodeChecker.isValid(id);
}
};
return {
/**
* Factory with callbacks for 'validate' directive.
* 0 arguments -- get all callbacks, over wise see #validateCallbacks object to get specific callbacks.
* if wrong callback name requested -> it will be ignored
* #returns {object} -- call requested callbacks.
*/
getValidateCallbacks : function() {
if(arguments.length) {
var result = {};
_.each(arguments, function(argument){
if(validateCallbacks[argument]) {
result[argument] = validateCallbacks[argument];
}
});
return result;
} else {
return validateCallbacks;
}
}
};
}]);
And code in controller looks like:
var validateCallbacks = UtilService.getValidateCallbacks('validateText', 'validateNumber');
_.each(validateCallbacks, function(callback, key) {
$scope[key] = callback;
});
I tend to use factories, specifically because factories can depend on other factories and logical modules can be defined. Consider the psuedo code below.
.controller('HisController', function (UtilityService) {
$scope.foo = UtilityService.foo;
})
.controller('HerController', function (UtilityService) {
$scope.foo = UtilityService.foo;
})
.factory('UtilityService', function (SomeOtherService) {
var service = {
foo: foo,
bar: bar,
}
return service
///////
function foo(a) {
return a.substring(0,1);
}
function bar(a) {
return SomeOtherService.doStuff(service.foo(a));
}
})
I am very newbie to AngularJS and sorry if it is a basic question.
I know that the services and factories are used for doing almost the same thing. They are used to get the external data.
But, is there any use case where we should use both in the same angular module? Just curious, as I was going through one of the github projects and realized they were using both services and factories for the same module. What is the advantage of doing it?
There's no practical difference, unless you prefer typing one way over another. I typically use services because I enjoy writing
this.myFunk = function(){...}
more than
myFunk: function(){....}
Whichever you prefer typing is the best way.
This is a great read that will clarify some of the differences between factories, services, and providers (which DO have different use cases than factories and services):
http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/
Sorry, just re-read your question. As far as I'm aware, there's no practical reason to use both in one module, as they accomplish the same thing. The only reason I can think for not using both services and factories in the same module is consistency in your code.
I think they originally wanted to make both those who like to new things and those who don't happy.
Personally we go with .factory() and like to use the Revealing Module Pattern to make it easy to see what methods / properties are available from that service/factory at a glance.
https://github.com/johnpapa/angularjs-styleguide#factories
this can end up with having to scroll all over trying to find what you can call from that "service"
/* avoid */
function dataService() {
var someValue = '';
function save() {
/* */
};
function validate() {
/* */
};
return {
save: save,
someValue: someValue,
validate: validate
};
}
instead use this
/* recommended */
function dataService() {
var someValue = '';
var service = {
save: save,
someValue: someValue,
validate: validate
};
return service;
////////////
function save() {
/* */
};
function validate() {
/* */
};
}