how to duplicate and make tiny changes to existing angularjs controller - angularjs

what I am trying to do here is to create a similar controller but not exactly the same, a few aspects of the controller need to be override. if it is the case in java then I probably just subclass and override the method I needed. but here in AngularJs not sure what is the best way to achieve this.
I really don't want to duplicate the code. refactoring the existing code is preferred but due to timeline issue have to delay that a bit.
so what is the correspondence of subclass and override in AngularJS

When you really need controller inheritance (including $scope), I think your question is best answered here: What's the recommended way to extend AngularJS controllers?

You can create a service and inherit its behaviour in any controller just by injecting it.
app.service("reusableCode", function() {
var reusableCode = {};
reusableCode.commonMethod = function() {
alert('Hello, World!');
};
return reusableCode;
});
Then in your controller that you want to extend from the above reusableCode service:
app.controller('MainCtrl', function($scope, reusableCode) {
angular.extend($scope, reusableCode);
// now you can access all the properties of reusableCode in this $scope
$scope.commonMethod()
});
DEMO PLUNKER: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview

Related

Angular: using templateUrl breaks compile-link ordering in directives

I've came across with problem exposing api of directives in order to its interaction with controller.
There is a simplified jsfiddle to describe my structure.
The problem is that directives has templateUrl property (I replaced it with template in fiddle above), it leads to loading templates async (correct behavior according to this question).
As a result directive's controller is called after main controller therefore directive's api function doWork is undefined (even if you wrap it call with something like $timeout):
.controller('MainCtrl', function($scope, $timeout) {
$scope.api = {};
$scope.init = function() {
$timeout(function() {
$scope.api.doWork();
})
}
$scope.init()
})
There is an approach that comes to my mind - use event in link function of directive and subscribe $scope.api.doWork to that event. But I'm not happy about using events. Also it's not clear how to handle such case if there are some directives nested to each other with similar way of exposing api.
On the other hand it's possible to replace templateUrl with template - but it's also quite bad decision in case of complex layout in template.
Hence, I stuck a bit with the way to resolve such kind of problem. What's the best way to fix it out? Probably there is another technique to expose directive's api (I was inspired by Andrej Kaurin answer in that thread)?
If you are using Angular 1.5+ and are using components you could try $postLink or $onInit function link. Otherwise you just create onDoWork directive scope property and then apply some function from main controller that will be fired when doWork will actually happen(since I think that directive should control when to doWork, if not then maybe you should just create service ?)

Creating custom filters in Angular

I am learning Angular and need to create some custom filters.
Do I create one filters.js file and put all my filters in there similar to all my reusable factory.js?
Eg: I have a utilsFactory.js and I put reusable functions in here.
Does this then get injected into the controllers? Or is this loaded in the $rootscope somewhere?
I have seen many examples on how to create them but not how to store and manage them and how to access them properly
filter.js
angular.module('achApp', [])
.filter('myUpperCase', function(){
return function(value){
return String(value).toUpperCase();
}
});
Controller
(function(){
var DevbController = function($scope, utilsFactory, $filter){
$scope.greeting = 'hello';
};
DevbController.$inject = ['$scope', 'utilsFactory', '$filter'];
angular.module('achApp')
.controller('DevbController', DevbController)
}());
Filters are generally created for use in your templates (HTML). For example:
<p>{{somethingOnScope | myFilter}}</p>
You can use them in your JS files by injecting $filter, and then getting a reference to your filter like: $filter('myFilter');
You can see usage examples in the filter documentation:
https://docs.angularjs.org/api/ng/filter/filter
Where and how exactly you register your filters is a matter of style. I personally try to follow john papa's advice and create only one injectable item per file.
On another subjective note, I prefer to minimize the use of filters, especially custom ones. If you want to use them primarily in your JS, a service/factory feels cleaner to me, and if you're tempted to create one for use in your templates, often you can instead just change the thing before putting it on the scope.

using $scope with controller as syntax

UPDATE
here's the article where I saw the examples below: https://github.com/johnpapa/angular-styleguide
after re-reading the example I realized that a specific and unique scenario was being explained, thus this question may not be fully valid. I will not delete it, however, so that if anyone has any further knowledge, they can contribute and help the community.
I've been reading about angular best practices and I'm convinced after reading a few articles that the best approach is to use the controller as syntax:
angular.module('example', [])
.controller('somectr', somectr);
somectr.$inject =['$http'];
function somectr($http) {
var vm = this;
this.x;
}
however I saw an article that shows this syntax, but it also injects a scope:
somectr.$inject = ['$scope', '$http'];
function somectr($scope, $http) {
var vm = this;
this.x;
this.y;
$scope.someFunc = function() {}
}
I thought that using the controller as syntax means no need to use a scope object. What user case would require the controller as syntax but still make use of a scope object?
An example of injecting $scope even if you are using controllerAs is when you want to pub/sub events.
If you want to Publish some events, you would use:
$scope.$broadcast('eventName', value)
$scope.$emit('eventName', value)
For Subscribing:
$scope.$on('eventName', function)
Take a further look at: https://docs.angularjs.org/api/ng/type/$rootScope.Scope
Another example would be if you are using $scope.$watch
There's a good article on controllerAs by ToddMotto: http://toddmotto.com/digging-into-angulars-controller-as-syntax/

Using Angular to Inject into non-Angular Objects

Is there a way to provide a non-Angular injection target to the Angular $injector such that Angular constructs like $http, $scope, $location or $q can be injected into it?
//non-angular injection container
var injectionTarget= {
$http:undefined,
$scope:undefined
}
//means to inject into target - this is the part in question
var injector = angular.injector();
injector.injectInto( injectionTarget, ["$http", "$scope"]);
I'm having the hardest time finding any info on how to accomplish what I would assume is a very sought-after feature.
I think that probably the easiest way to do this would be to register your objects as services with the module.
var myObject = {} //Defined elsewhere or here as empty
app.service(‘aReferenceName’, function($http){
myObject.$http = $http;
return myObject;
});
This would have the double effect of setting the properties you want on your object, and making it accessible from angular as needed. It's also a pretty simple block of code. Note the implication though that as a service it would be a singleton from angular's perspective. If you need to do it as a class with many instances, you'll be wanting a factory.

angular js how can I use a collection in all of my scopes

I have a collection that I need to access in all of my nested scopes. Inside my directive templates, inside my directives in ng-repeat... n levels deep. I don't want to have to say $scope.$parent.$parent.$parent....$parent.MyList.
I've tried using $rootScope, but clearly I lack the understanding of how this works. I pass it into my directive during the declaration like so:
$rootScope.MyList = ["list": 1];
...
...
MyApp.directive('mydirective', ['$rootScope', function ($rootScope) {
return {
restrict: 'A',
replace: false,
link: function (scope, rootScope) {
}
}
}])
The rootScope does not contain MyList. Is there something I'm doing wrong, or a better way to do it? I've thought of using a Factory or Service, but I don't know how to set that up and we all know how crappy the documentation is for Angular, so searching is very frustrating.
Can you provide a plnkr or jsfiddle reproducing this behavior? I can get the controller to communicate with service using $rootScope.
http://plnkr.co/edit/IEhOde
However, you'd probably want to create a service which would cache the value that you want and inject it in your directive and retrieve the value.
First of all $rootScope.MyList = ["list": 1] has syntax error, should be $rootScope.MyList = ["list", 1] or $rootScope.MyList = [{"list": 1}].
Anything you assign to $rootScope is accessible within child $scopes (except isolate scopes) due to prototype inheritance.
Generally if you want to share something within whole app, you can assign it to $rootScope as you did or you can create top level AppCtrl which will have something like $scope.MyList=... and all nested scopes will have access to it. Second option is bit better when it comes to testability as you can inject service to AppCtrl which provides MyList data. You can see this pattern in action here AngularJS GlobalCtrl vs $rootScope vs Service.
A service/provider is the recommended approach.
For just a list of data you can use the value provider:
app.value('listService', {
MyList : {"list": 1}
});
Inject it where you need it, and you can include it in the relevant scopes as such:
app.controller('Ctrl', function($scope,listService) {
$scope.list = listService.MyList;
});
A fiddle of that: http://jsfiddle.net/qKcQL/
Injecting it where you need is good for documentation and maintenance. But if you really need this on the global $rootScope then this will accomplish that:
app.controller('Ctrl', function($scope,listService,$rootScope) {
$rootScope.list = listService.MyList;
});
$rootScope fiddle: http://jsfiddle.net/qKcQL/1/

Resources