Extending an object in javascript/angular - javascript

So I have a tree helper object in my angular app that provides a bunch of useful functions for dealing with the many tree structures in the app. This is provided by a factory, so each time some controller (or whatever) asks for a tree helper, it gets its own copy:
angular.module('MainApp').factory('TreeHelperFactory',
function ($http, $q, $filter, DataService) {
var treeHelper = new function ($http, $q, $filter, DataService) {
...
code
...
})
Now I have a category service that provides various category-related functions, including returning a tree of categories and providing ways to manipulate that tree. So thinking like an OO developer, I reckon that the category service is really like a subclass of my tree helper, so I inject a tree helper instance and then attempt to extend that instance with category-specific functions, naively like this:
angular.module('MainApp').provider('CategoryService',
function() {
this.$get = function ($http, $q, $filter, DataService, TreeHelperFactory) {
var catService = TreeHelperFactory;
catService.listCategories = function() {...}
catService.deleteCategory = function(id) {...}
... more code...
return catService;
}
}
);
But this doesn't work. The new properties are not visible when I try to invoke them later, only the tree helper properties of the original factory object. What am I missing and how do I achieve this?

Services in angular.js are singletons, which means each time you inject a service it returns the exact same object, you cannot force it to return a new object each time it is injected.
What you can do is to create a Class function, for example:
angular.module('MainApp')
.factory('TreeHelperFactory',
function ($http, $q, $filter, DataService) {
/**
* TreeHelper constructor
* #class
*/
function TreeHelper() { /* this.init(); */ }
/**
* TreeHelper static class method
*/
TreeHelper.list = function() { /* ... */ };
/**
* TreeHelper instance method
*/
TreeHelper.prototype.init = function() { /*...*/ };
/**
* Returns the class
*/
return TreeHelper;
})
Now you can inject it, instantiate an object and extend it like so:
.factory('CategoryService', function (TreeHelperFactory) {
var catService = new TreeHelperFactory();
catService.listCategories = function() { /*...*/ };
catService.deleteCategory = function(id) { /*...*/ };
return catService;
});
You can also use javascript prototypical inheritance but I think it's an overkill for most cases so keep it simple if you can.

Helper that depends on Angular services is a bad smell for me - almost like its status is more than a helper and it has a service or two in it. Would you be able to rethink this through restructuring their roles and responsibilities?

Related

Real inheritance in JavaScript

I tried to implement prototypical inheritance between two AngularJS services and one generic service, but this seems not enough to serve my needs.
I made ChildService, SecondChildService and ParentService.
ChildService extends ParentService with prototypical inheritance
SecondChildService extends ParentService with prototypical inheritance
ParentService has a method let's call it logIt() that logs property it
ChildService implements its own version of it
SecondChildService implements its own version of it
Now I consider ParentService as the "base class", and I have a method within ParentService that calls logIt(), that is initialize(). This is only implemented in ParentService
I need to run ChildService.initialize() and see that the it of ChildService gets logged.
The same thing with SecondChildService.
Instead the ParentService logs its own version of it. The reason I believe is that prototypical inheritance is a composition implementation rather than real inheritance
Can you please propose a workaround?
Edit:
This is my parent service
angular.module('common').service('parentService', ParentService);
ParentService.$inject = [];
/* #ngInject */
function ParentService() {
'use strict';
var vm = this;
vm.it = "parent";
vm.initialize = function () {
vm.logIt();
};
vm.logIt = function () {
console.log(this.it);
};
}
This is my child service
angular.module('common').service('childService', ChildService);
ChildService.$inject = ['parentService'];
/* #ngInject */
function ChildService(parentService) {
'use strict';
var vm = this;
angular.extend(this, parentService);
vm.it = "child";
}
And this is what I get in the log when I run childService.initialize()
parent
I am expecting to get child instead
I can't reproduce your problem. Inheritance seems to work correctly in the following example:
class Main {
do_it() {
console.log("Main do_it");
this.it();
}
it() {
console.log("Main it executed");
}
}
class A extends Main {
it() {
console.log("A it executed");
}
}
var a = new A;
a.do_it();
console.log("Done");
The do_it method that is defined in the Main class correctly calls the it method of the extended class which has overridden the it method of the parent class.
The problem is in ParentService. I am using vm.logIt, while I should be using this.logIt in order to get the it from the child service scope. Contrary to standard inheritance, it seems that in prototypical inheritance the logIt() function exists twice, once in the ParentService and once in the ChildService (inherited from parent). With this.logIt() you can call it from the child and it actually gets the it from the child, while with vm.logIt() you call it from the parent where it gets the it from. It is a matter of scope.
This is the correct implementation of ParentService:
angular.module('common').service('parentService', ParentService);
ParentService.$inject = [];
/* #ngInject */
function ParentService() {
'use strict';
var vm = this;
vm.it = "parent";
vm.initialize = function () {
this.logIt(); // <-- this changed
};
vm.logIt = function () {
console.log(this.it);
};
}

angular.factory is not a function

I have this factory and I have the error in the title. How can I fix this?
(function (angular,namespace) {
var marketplace = namespace.require('private.marketplace');
angular.factory('blueStripeViewModelFactory', [
'$http', function blueStripeViewModelFactory($http) {
return new BlueStripeViewModel("id", 10);
}
]);
marketplace.blueStripeViewModelFactory = blueStripeViewModelFactory;
})(window.angular,window.namespace);
You can't create an independent factory as it should be created within a module.
Example,
angular.module("myFactoryModule").factory("blueStripeViewModelFactory",function(){
// define your factory
});
Now you are able to use this factory in any of your module. To use this module, you just need to add an injector of your factory module (here, myFactoryModule).
Example,
angular.module("anotherModule", ["myFactoryModule"]);

How can i pass scope in template in angular js

I have one BaseController with common functions which my all other controllers inherit .
The controller is like this
function BaseController () {
this.defaultFilters = {};
this.doStuff = function ($scope) {
$scope.myobj.value = 1;
this.otherfunction();
};
I inherit that in my controller like this
BaseController.call($scope);
Now in my do stuff function i need to pass $scope because myobj is only visible there.
Now i want to know that how can i pass that in my template because i want to call that function when some click on some button
ng-click="doStuff(scope)"
Everything that you associate with your controller's scope, so you just associate your scope with some variable and i guess that will do the job.
Something like this :
app.controller(function($scope) {
$scope.scope = $scope;
});
But if you go by some standard approach, i suggest moving these common functions inside some service, injecting this service into each controller and using it in the views.
Something like this :
app.service("constantService", function() {
this.data = {}; // This will represent your common data.
this.commonFunction = function() {
};
});
app.controller(function() {
$scope.constantService = constantService;
// You can now use $scope.constantService.data as your reference for data, and then can copy it to some local $scope variable whenever required.
});
ng-click="constantService.commonFunction()"

JavaScript service: how to provide a () method, like an object constructor?

I would like to create a service that will create an interface to AngularJS's $http based on this. I would like to use dependency injection to inject the service without re-writing individual function calls and keeping the code clean. To do this I am providing a common service that creates a variable to the $http object like this:
commonService = function(...) {
return {
...
$http: $http
}
Then I use common.$http everywhere in the code. To change $http from AngularJS $http to my httpService, I need to only change it in one place. httpService looks like this:
function httpService($http, $q, pendingRequests, $interval) {
var get = function(url) {
...
return requestPromise;
};
var service = {
get:get
};
return service;
}
This works for calls to $http.get() but what about calls to $http({method:'get', url:'...'}); ?
Is it possible to provide the () method, which is really httpService()()? Otherwise it would call the AngularJs method httpService().
You can just return a function instead of an object. You can add the properties to your function
function httpService($http, $q, pendingRequests, $interval) {
var get = function(url) {
...
return requestPromise;
};
// Instead of returning an object, return a function
var service = function() {
// do whatever you want here
};
service.get = get;
// Add more properties to service as needed
return service;
}

How to create helper class that can accessed with controller on AngularJS

how can I create a helper/utility class that can be accessible from the multiple controllers?
For example, I have two controllers: UpdateItemCtrl and CreateItemCtrl. These have common functions inside which increases redundancy and lowers managability.
I'd like to create a ItemSaveHelper class which I would put the common methods inside and call them from the active controller.
You want to create a service.
A service is just a singleton that can be injected into different things to provide modular/shared functionality. Here's a simple example: http://jsfiddle.net/andytjoslin/pHV4k/
function Ctrl1($scope, itemManager) {
$scope.addItem = function(text) {
itemManager.items.push(text);
};
}
function Ctrl2($scope, itemManager) {
$scope.items = itemManager.items;
}
app.factory('itemManager', function() {
return {
items: []
};
});

Categories

Resources