i create a directive and it is reusable. but when i use it in a controller scope not work.
example:
<my-directive ... > </my-directive>// work correctly
<div ng-controller='myController'>
<my-directive ... > </my-directive> // not work
</div>
<my-directive ... > </my-directive>// work correctly
Update question
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
url: '#',
name:'#'
//other ...
},
controller: ['$scope','$http', function($scope,$http){
}],
link: function(scope, element, attrs){
}
Related
I am trying to figure out how to pass a transclusion down through nested directives and bind to data in the inner-most directive. Think of it like a list type control where you bind it to a list of data and the transclusion is the template you want to use to display the data. Here's a basic example bound to just a single value (here's a plunk for it).
html
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data"><div>{{ source.name }}</div></outer>
</body>
javascript
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
ctrl.welcomeMessage = 'Welcome to Angular';
}])
.directive('outer', function(){
return {
restrict: 'E',
transclude: true,
scope: {
model: '='
},
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template :'<div class="inner" my-transclude></div>'
};
})
.directive('myTransclude', function() {
return {
restrict: 'A',
transclude: 'element',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
element.after(clone);
})
}
}
});
As you can see, the transcluded bit doesn't appear. Any thoughts?
In this case you don't have to use a custom transclude directive or any trick. The problem I found with your code is that transclude is being compiled to the parent scope by default. So, you can fix that by implementing the compile phase of your directive (this happens before the link phase). The implementation would look like the code below:
app.directive('inner', function () {
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template: '<div class="inner" ng-transclude></div>',
compile: function (tElem, tAttrs, transclude) {
return function (scope, elem, attrs) { // link
transclude(scope, function (clone) {
elem.children('.inner').append(clone);
});
};
}
};
});
By doing this, you are forcing your directive to transclude for its isolated scope.
Thanks to Zach's answer, I found a different way to solve my issue. I've now put the template in a separate file and passed it's url down through the scopes and then inserting it with ng-include. Here's a Plunk of the solution.
html:
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data" row-template-url="template.html"></outer>
</body>
template:
<div>{{ source.name }}</div>
javascript:
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
}])
.directive('outer', function(){
return {
restrict: 'E',
scope: {
model: '=',
rowTemplateUrl: '#'
},
template: '<div class="outer"><inner my-data="model" row-template-url="{{ rowTemplateUrl }}"></inner></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
scope: {
source: '=myData',
rowTemplateUrl: '#'
},
template :'<div class="inner" ng-include="rowTemplateUrl"></div>'
};
});
You can pass your transclude all the way down to the third directive, but the problem I see is with the scope override. You want the {{ source.name }} to come from the inner directive, but by the time it compiles this in the first directive:
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
the {{ source.name }} has already been compiled using the outer's scope. The only way I can see this working the way you want is to manually do it with $compile... but maybe someone smarter than me can think of another way.
Demo Plunker
I am wondering how to implement the scope inherit between directives.
For example:
<html ng-app="app">
<head>
<title>TEST DRAG</title>
</head>
<body ng-controller="main">
<dragcont>
<dragitem></dragitem>
</dragcont>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script type="text/javascript">
(function(){
var app = angular.module("app", []);
app.controller("main", function($scope){
$scope.name = "Hello";
})
.directive("dragcont", function(){
return {
restrict: "AE",
scope: {
},
controller: function($scope){
$scope.name = "dragcont";
},
link: function(scope, EL, attrs){
}
}
})
.directive("dragitem", function(){
return {
restrict: "AE",
controller: function($scope){
console.log($scope.name);
},
link: function(scope, EL, attrs){
}
}
})
})()
</script>
</body>
</html>
When I run this, it always prints Hello. It seems that dragitem can inherit the scope from main controller, but what if I want it to inherit from dragcont?
Isolate scope is used to "isolate" the inner workings of a directive from its usage. As such, the scope neither inherits from its parent, nor can be inherited from by the child directives and expressions.
So, for the isolate foo directive:
.directive("foo", function(){
return {
scope: {},
link: function(scope){
scope.inner = "hidden from outside";
}
}
})
the child directives and expression will not inherit its isolate scope.
<foo>
<span>{{inner}} will be undefined</span>
</foo>
Using a template:
On the other hand, a template of a directive foo is known to the author of the directive, and so it does use the isolate scope. The following would have worked, if foo had a template:
scope: {},
template: '<span>{{inner}}</span>',
link: function(scope){
scope.inner = "hidden from outside";
}
Using manual "transclusion":
Occasionally, it makes sense to allow the user of the directive to specify a custom template. The author of the directive may also want to expose special "magic" variables to use in the custom template, not unlike $index, $first, etc.. of ng-repeat.
This can be done with a manual transclusion:
scope: {},
transclude: true,
template: '<div>{{header}}</div>\
<placeholder></placeholder>',
link: function(scope, element, attrs, ctrls, transclude){
scope.header = "I am foo"; // still only visible in the template
// create a new scope, that inherits from parent, but a child of isolate scope
var anotherScope = scope.$parent.$new(false, scope);
anotherScope.$magic = "magic";
// transclude/link against anotherScope
transclude(anotherScope, function(clonedContents){
element.find("placeholder").replaceWith(clonedContents);
}
}
Now, you can have access to $magic variable inside the transcluded contents and to the outer scope (assuming it has $scope.name = "John")
<foo>
<div>I can see {{name}} and {{$magic}}</div>
</foo>
The resulting DOM will be:
<foo>
<div>I am foo</div>
<div>I can see John and magic</div>
</foo>
It looks like you are still missing some work to be able to make a directive inherit from another.
I think this code will help you:
http://codepen.io/anon/pen/EaPNqp?editors=101
Also, you might want to read:
http://david-barreto.com/directive-inheritance-in-angularjs/
CODE:
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.data1 = "1";
$scope.data2 = "2";
})​var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.data1 = "1";
$scope.data2 = "2";
})
.directive('myWrapper', function() {
return {
restrict: 'E'
, transclude: true
, scope: true
, template: '<h1>{{ title }}</h1><ng-transclude></ng- transclude><h2>Finished wrapping</h2>'
, controller: function($scope, $element, $attrs){
$scope.title = $attrs.title;
$scope.passdown = $attrs.passdown;
}
};
})
.directive('myInner1', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
})
.directive('myInner2', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
});
.directive('myWrapper', function() {
return {
restrict: 'E'
, transclude: true
, scope: true
, template: '<h1>{{ title }}</h1><ng-transclude></ng- transclude><h2>Finished wrapping</h2>'
, controller: function($scope, $element, $attrs){
$scope.title = $attrs.title;
$scope.passdown = $attrs.passdown;
}
};
})
.directive('myInner1', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
})
.directive('myInner2', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
});
which is found very useful. Make sure you read the comments below the article as well.
Pay attention to the "require" property.
Regards.
I have a problem with this:
app.directive('myDirective', function(){
return{
retrict: 'EA',
replace: false,
scope:{
fstData: '#',
sndData: '#'
},
template: '<div ng-controller="myController" arg="{{fstData}}"><h3>{{sndData}}</h3><li ng-repeat="event in eventsCat"></li></div>'
}
});
When I create a my-directive tag in the HTML, it doesn't bind fstData but if I delete {{fstData}}, and I put something, it works.
I think that I can't binding in a tag that contains a ng-controller attribute, but I need this attribute (args) because in myController I use it.
Thanks!
In myController I have this:
app.controller('myController', function($scope, $attrs){
var myVar = myArray[$attrs.arg];
Have you tried this approach?
app.directive('myDirective', function(){
return{
retrict: 'EA',
replace: false,
scope:{
fstData: '#',
sndData: '#'
},
link: function(scope ,element , attrs)
{
var markup = '<div ng-controller="myController" arg="'+scope.fstData+'"><h3>'+scope.sndData+'</h3><li ng-repeat="event in eventsCat"></li></div>' ;
element.append(markup);
}
}
});
I have this code, written with Angular 1.2: http://jsfiddle.net/VmkQy/1/
<div ng-app="app">
Title is: <span my-directive data-title="Test title">{{ title }}</span>
</div>
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: {title:'#'},
link: function($scope) {
alert($scope.title);
}
}
}])
;
Scope has a title property, but it does not rendered. Why?
If I change directive config to scope:true, it will works fine: http://jsfiddle.net/VmkQy/2/
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: true,
link: function($scope, $element, attrs) {
$scope.title = attrs.title;
alert($scope.title);
}
}
}])
;
This is a bug or feature in Angular 1.2? Older version works fine in all this cases: http://jsfiddle.net/VmkQy/3/
The {{title}} inside of your <span /> gets replaced. Add template: "{{title}}" to your directive and it works:
http://jsfiddle.net/VmkQy/5/
With HTML like this...
<div ng-app="myApp">
<div ng-controller="inControl">
I like to drink {{drink}}<br>
<input my-dir ng-model="drink"></input>
</div>
</div>
and javascript like this...
var app = angular.module('myApp', []);
app.controller('inControl', function($scope) {
$scope.drink = 'water';
});
app.directive('myDir', function(){
return {
restrict: 'A',
link: function($scope, element, attrs, ctrl) {
// why is this logging undefined?
console.log(ctrl);
}
};
});
Why can I not access the controller from within my directive? Why is my call to ctrl giving me undefined?
EDIT: add demo...
Fiddle available here: http://jsfiddle.net/billymoon/VE9dX/
see multiple controller can be attached with one app and simillarly multiple directive can be attached with one app, so if you wants to use one controller in one directive than you can set the controller property of directive to the name of the controller you wants yo attach with like in your case
app.directive('myDir', function(){
return {
restrict: 'A',
controller: 'inControl'
link: function($scope, element, attrs, ctrl) {
// why is this logging undefined?
console.log(ctrl);
}
};
});
Despite this working with require:ngModel, this still isn't the best approach as it ties the directive directly to the controller. If you want your directive to communicate with your controller, you could be setting and reading off the scope.
HTML:
<div ng-app="myApp">
<div ng-controller="inControl">
I like to drink {{drink}}<br />
<input my-dir="drink"></input>
</div>
</div>
JS:
var app = angular.module('myApp', []);
app.controller('inControl', function($scope) {
$scope.drink = 'asdfasdf';
});
app.directive('myDir', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
console.log(scope[attrs.myDir]);
}
};
});
Alternatively you can use my-dir="{{drink}}" and read it as attrs.myDir.
http://jsfiddle.net/8UL6N/1/
Adding require: 'ngModel', fixed it for me - not sure if there is another way to specify it...
app.directive('myDir', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, element, attrs, ctrl) {
// why is this logging undefined?
console.log(ctrl);
}
};
});