Angular.js Directive controller inheritance - javascript

Assume that I have a directive:
.directive('money', ['Service', function (Service) {
/*
* Some code
*/
controller: ['$scope', '$element', '$attrs', '$parse', function (scope, cElement, attrs, $parse) {
scope.someValue=1;
scope.someFunction = function(){
console.writeline('money');
}
}
and there is a second directive:
.directive('cash', ['Service', function (Service) {
/*
* Some code
*/
controller: ['$scope', '$element', '$attrs', '$parse', function (scope, cElement, attrs, $parse) {
scope.someValue=1;
scope.someFunction = function(){
console.writeline('cash');
}
}
As you can see only difference between those two directives is content of a one function.
So perfect way would be inherit all the controller and shadow that someFunction
Is it even possible in angular to make something like this or I should leave two directives with so small diferences?

Why not just have a console directive that grabs what to write from an attribute on the directive's element?
So, instead of a <div data-money></div> and <div data-cash></div> You'd have <div data-console text="money"></div> and <div data-console text="cash"></div>.
Basically, pull what's different into attributes that can be brought into a more generic directive.
Based upon comments, how about this? Create the controller as a standalone function, then use that same controller in both directives.
At this point though, I'm not sure it's going to save you much (or any) code and may be overkill for this refactoring. Considering the minor differences, it may make more sense to just keep it the way you have it.

Yes, there are options for inheritance, It was discussed here before:
AngularJS controller inheritance
Edit:
in addition you can take the common functionality and share it through an injected service, the variations may be passed as a parameter.

Related

Angular Controller on Custom Directive

Here is my controllers.js file
(function(ctx,angular){
'use strict';
angular.module('app.controllers')
.controller('SearchMasterController',['$scope',function($scope){
//My Code
}]);
})(window, angular);
And this is my directives.js file
(function(ctx,angular){
function ControllerFunction(){
//My Controller Code
}
var directiveConfig = {
restrict:'E',
templateUrl:'path/to/acco.html',
controller: ControllerFunction
}
angular.module('app.directives')
.directive('acco', function(){
return directiveConfig;
});
})(window, angular);
Now my question is, can I use this acco directive with some different controller. Ideally, is there any way to get it working like
<acco ng-controller="SearchMasterController"></acco> ?
I tried doing,
<acco>
<div ng-controller="SearchMasterController"></div>
</acco>
and it seems to work.
Is it possible to use
<acco ng-controller="SearchMasterController"></acco> ?
The latter alternative looks ugly to me.
nice to heard this type of access, i have tried
<acco>hi{{name1}}
<div ng-controller="SearchMasterController">{{name1}}</div>
</acco>
<acco ng-controller="SearchMasterController">{{name1}}</acco>
<script>
angular.module('myApp', [])
.controller('SearchMasterController', ['$scope', function ($scope) {
//My Code
console.log("search");
$scope.name1 = 'james';
}])
.directive('acco', function () {
return{
restrict: 'E',
templateUrl: 'acco.html',
controller: function($scope) {
//My Controller Code
console.log("cntrlr fn");
$scope.name1 = 'semaj';
}
};
});
</script>
#that time i getting output as
cntrlr fn
search
cntrlr fn
means if we are using like
<acco>hi{{name1}}
<div ng-controller="SearchMasterController">{{name1}}</div>
</acco>
then we can access both controllers but when we are using like
<acco ng-controller="SearchMasterController">{{name1}}</acco>
we can't access SearchMasterController and it's not loaded also..
Yes you can use some different controller for your directive, but there is some best practice
Use controller when you want to expose an API to other directives. Otherwise use link.
The way you tried to use controller doesn't make much sense
<!--here acco and ng-controller both are directives,
in your directive's 'ControllerFunction' and ng-controller's 'SearchMasterController'
has the same controll (scope) for 'acco' rendered html.
In that case your directive's controller overrite ng-controller functionality.
So leave 'ng-controller',
if you need any functionality in your directive
then pass those functionality using =,&,#-->
<acco ng-controller="SearchMasterController"></acco>

JS - AngularJS - Get data of an directive from a controller

I´ll give my best to make my problem understandable:
I have a directive. This directive handles/alters also some data it displays. Now I was wondering how I can access these data from a parent scope e.g. the controller. I am currently getting access to the scope by selecting the element and ask for the corresponding scope:
element.scope()
It works fine but it occurs to me that this is kind of a - lets say - unconventional or odd way to get the scope of an directive.
I apologize for my english (I still practicing) and hope you guys can help me. Thank you in advance :)
OK, Based on the comments your directive is some kind of a form. There are two approaches for controlling the data inside a directive:
Parent controller of a directive should control the data. In that case, Data of each directive is unique and does not share between all directives.
The directive itself should control the data. Which it means the data will be shared in all directives, and if You change one, another will be changed too.
I go with the solution number 1 which is more preferable in my guess. Let's say your template of directive is something like this,We call it template.html:
<form name="directive_from" ng-submit="submit_form()">
<input type="text" ng-model="form.name" name="username" placeholder="username"><br>
<button type="submit">Submit</button>
</form>
Because of the time,I skipped validation but you can do that easily :)
And the directive:
angular.module('app')
.directive('formDirective',function(){
return {
restrict: 'E',
templateUrl: 'template.html',
scope: {
form: '=formData',
submit_form: '#formFunc'
},
controller: ['$scope', function($scope) {
//some extra logic or binding if you need
}
}
})
OK,Now our directive which we call it formDirective accepts two parameters:
formData which is an object and holds user inserted data.
formFunc which is a function for submitting the form.
Now parent controller:
angular.module('app')
.controller('MainCtrl', ['$scope', function($scope) {
$scope.form_object = {};
$scope.submit_it = function() {
//Do whatever you like with the $scope.form_object
}
}
And let's see the view:
<div ng-controller="MainCtrl">
<form-directive form="form_object" form-func="submit_it()"></form-directive>
</div>
That's it! It's so simple example that I believe you can customize it by your needs,But the main logic for controlling the data is something like that. for example you can pass a function for ng-change on the input field or add more input fields or...
#lilly: I think you you are looking for data exchange between the parent scope and isolated scope which is possible via '='
Here is the working fiddle
angular.module('App', [])
.controller('Main', ['$scope', function ($scope) {
$scope.exchange = {};
$scope.exchange.value = "initialized by controller";
}])
.directive('inputValue', function($timeout) {
return {
restrict: 'A',
scope: {exchangeValue: '='},
link: function(scope, element, attrs) {
$timeout(function(){
scope.exchangeValue="changed by directive";
},2000);
}
};
});
http://jsfiddle.net/tiru/dJty6/43/

Isolate scope doesn't work the way I think it should

I have a fundamental misconception about isolate scopes and I can't figure out what it is.
I have a controller and directive:
.controller('MyCtrl', [
'$scope',
function($scope) {
$scope.zed = "ZZZZZ";
$scope.zz = function() {
return "yep";
};
}])
.directive("myDirective", function() {
return {
restrict: "AE",
controller: "MyCtrl",
templateUrl: 'a/path/to/my_template.html',
scope: {
z: '#'
}
};
and the template:
<div>***{{z}} {{zz()}} ^^^ {{zed}} </div>
and the use of the directive:
<div my-directive z="yabba"/>
When I run this app, the value of 'yabba' is displayed for z. I'm good with that. The isolate scope makes this possible. However, the template reaches right into the $scope and is able to run a function and grab a scalar (zed and zz). I don't want that. The controller needs access to $scope since the model upon which I'll eventually operate has to be assembled from $scope data.
My desire is to limit the directive to as little information as possible.
I would like the $scope to be available to the controller but not the directive. I want the directive to have to gets its data from the controller exclusively. I tried to add z and zed to the isolate scope but this did nothing helpful.
How can I do this? Or, is my approach simply foreign to Angular and bad? If so, that's cool, please explain to me the better approach. And, please, use small words.
You are stepping on your toes by including your controller in the directive. Just delete this line from your directive and it will behave as you guessed: controller: "MyCtrl",
Here is a plunker to mess around with. You can see that your isolated scope is working as it should with the line commented out. Just un-comment that line again and see how you now have access to the controller scope.
.directive("myDirective", function() {
return {
restrict: "AE",
// controller: "MyCtrl", REMOVE THIS LINE!!!
templateUrl: 'a/path/to/my_template.html',
scope: {
z: '#'
}
};
Plunker playground with your code
By the way, there is nothing foreign in your code. Isolated scope is a great feature to use with custom directives. There are many ways to work with Angularjs 1.+. This may not be the case when Angularjs 2.0 is released. In your example, the controller is not needed within the directive. I rarely include controllers, and I most always isolate scope. Good luck! Hope this helps.

ngBind equivalent of NgModelController (or best practice)

http://plnkr.co/edit/C4mFd5MOLBD2wfm8bMhJ?p=preview
Let's take a simple example and say you want to display the value of a cookie regardless of what it is, but this could be a customer name or whatever you want. There seem to be so many options available: directives, services, directives with services, controllers - and no matter how many docs I review or blog posts I read, I still have some fundamental questions about the appropriate way to access data and then update the scope accordingly.
What's clouding my thought right now is the fact that there doesn't seem to be the equivalent of NgModelController for non ngModel capable DOM elements like span or div or anything besides user input. Basically, seeing how ngModelCtrl is utilized in the link function of a directive seems to make a lot of sense, it doesn't allow you to drown in scope soup and it nicely organizes your thoughts, but how do we achieve this decoupling with ngBind elements?
I think the answer is just 'use services', but perhaps maybe not in all cases is the thing that's gnawing at me. Suppose you want to display a very specific cookie (or a customer name) and you don't know where you want to display it, you could continually inject your custom CookieService where ever you go, but what about a specific directive that cleans things up: <specific-cookie></specific-cookie> Would we just inject our CookieService into that directive, or just access it via $cookies like we've done elsewhere.
Does the answer simply lie in whether or not you'll be accessing more than one cookie in the future? That is, if you only need one <specific-cookie></specific-cookie>, then just use $cookies in you're directive and move on with your life, or it is always appropriate to abstract away this type of call into a service, or am I just being super pedantic about understanding this.
Directive
angular-myapp.js
var app = angular.module('myApp', ['ngCookies']);
app.directive('cookie', ['$cookies', function($cookies) {
return {
scope: true,
controller: function($scope, $element, $attrs) {
$scope.cookie = $cookies[$attrs.cookie];
}
}
}]);
index.html
<div cookie="__utma">Cookie: {{cookie}}</div>
Controller
angular-myapp.js
app.controller('CookieCtrl', function($attrs, $cookies) {
this.value = $cookies[$attrs['getcookie']];
});
index.html
<a ng-controller="CookieCtrl as cookie" getCookie="__utma" href="/{{cookie.value}}">{{cookie.value}}</a>
Service
angular-myapp.js
app.controller('SomeCtrl', function($scope, CookieService) {
$scope.cookie = CookieService.getCookie('__utma');
});
app.service('CookieService', function($cookies) {
var getCookie = function(cookie) {
return $cookies[cookie];
};
return ({ getCookie: getCookie });
});
index.html
<div ng-controller="SomeCtrl">Cookie: {{cookie}}</div>
Directive with service
angular-myapp.js
app.directive('specificCookie', function(CookieService) {
return {
scope: true,
template: 'Cookie: <span ng-bind="cookie"></span>',
controller: function($scope, $element, $attrs) {
$scope.cookie = CookieService.getCookie('__utma');
}
}
});
index.html
<specific-cookie></specific-cookie>
Unless I'm misunderstanding some of your scenarios, the simplest (and proper) way to do this is to create a reusable directive that displays a cookie based on a name passed to it via its attribute.
app.directive('cookie', ['$cookies', function($cookies) {
return {
scope: {},
template: "<span>{{cookie}}</span>",
restrict: "E",
link: function(scope, element, attrs) {
attrs.$observe("name", function(newVal){
scope.cookie = $cookies[newVal];
});
}
};
}]);
The usage would be trivial (and page controller-independent):
<cookie name="__utma"></cookie>
<input ng-model="cookieName" type="text">
<cookie name="{{cookieName}}"></cookie>
the resulting DOM would be:
<span class="ng-binding">137862001.838693016.141754...</span>
<span class="ng-binding">GA1.2.838693016.1417544553</span>

Angular Service Injection

I am working on a trivial task (that I got working) which adds an an .active class to dom elements in a tab nav. Simple. Got it working by following https://stackoverflow.com/a/12306136/2068709.
Reading over the answer provided and comparing their controller to example controllers on angular's website I came across something interesting that I don't quite understand.
On the AngularJS website, $scope and $location (and other services) are injected into the controller along with an anonymous function which defines the controller. But on the answer, the services are not injected, rather they are passed via the anonymous function. Why does this work? Shouldn't the service always be injected?
In terms of an example: Why does this
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
}])
and this
angular.module('controllers', [])
.controller('NavigationController', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
})
both work?
This may be trivial but its throwing my brain for a loop that I can't figure out.
The two examples are equivalent - they just make use of different syntax. The first example uses what they call "inline array annotation" (see here). The purpose of this alternate syntax is just to allow a convenient way to make the injected variable names different than the name of the dependency.
So for example, if you wanted the variable names to be "s" and "l", then you could write:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function(s, l) {
s.isActive = function(route) {
return route === l.path();
};
}]);
Actually they are injected in both cases, the difference between those two cases is in the first scenario you define and name the dependency this could be useful if you minify your js code and that way you are declaring explicitly the dependency for examply it could be:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($s, $l) {
$s.isActive = function(route) {
return route === $l.path();
};
}])
that way angular will know which dependency to inject on which parameter without looking at the naming for each parameter.
the other case you need to be explicit and declare which dependency you'll inject by setting up the name.
I hope that helps.
This is how angular handles code minification. by keeping strings intact it can keep mapping vars when they are renamed.
if you take a look at the code of the controller https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L48
you'll see that the constructor can accept both function and array.

Categories

Resources