How to use custom directives. - angularjs

I have mentioned HTML code below
<div class="panel" ng-controller="myController">
<div myArr="arr" my-Dir="">
</div>
JavaScript is
app.controller('myController',['$scope',function($scope){
// here i have one array of objects arr is here
}])
and Directive is
app.directive("myDir", function() {
scope: {
myArr: "=",
},
templateUrl: "views/myHTML.html"
};
});
Here I am including myHTML.html file. How do I pass the array to this file?. I need this array in myHTML.html file. I want to display the array elements in the myHTML using the ng-repeat.

You are using dayArr in the html and then trying to isolate your scope on something called dayArray. These don't match up. You also need to pass the array into dayArr so that it can actually have a value in your scope
I've updated the fiddle to have a working example http://jsfiddle.net/zpWsz/2/
This is the relevant bits:
Here is where I pass in some dummy array.
<div my-dir="" day-arr="[1,2,3]" >
Here is the updated directive pieces:
scope: {
dayArr : "="
},
template: '<ul>' +
'<li ng-repeat="i in dayArr">{{ i }}</li>' +
'</ul>',
A templateUrl would work too, but how you had it in the fiddle was throwing js errors.
Hope this helped.

custom directive template
app.directive('myDir', function(){
return {
scope: {
myArr: "=",
},
restrict: 'EAC',
templateUrl: 'views/myHTML.html',
link: function(scope, element, attr) { }
};
});
And to use it
<div data-my-dir data-my-arr="arr">

Related

Directive with isolated scope and added properties, not available to inner directives

I'd like to have a directive with an isolated scope, and to set properties to this scope from within the directive. That is to create some environment variables, which would be displayed by other directives inside it, like so:
HTML:
<div environment> <!-- this directive set properties to the scope it creates-->
{{ env.value }} <!-- which would be available -->
<div display1 data="env"></div> <!-- to be displayed by other directives (graphs, -->
<div display2 data="env"></div> <!-- charts...) -->
</div>
JS:
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: {},
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
require: '^environment'
scope: {
data: '='
},
link: function(scope, elt, attr, envController) {
scope.$watch('data', function(oldV, newV) {
console.log("display data");
});
}
};
})
.directive("display2", function() {
return {/* ... */};
});
But it doesn't work. Here is a Plunker.
If I remove the isolation, it works ok though. What do I do wrong ? Is it a problem of transclusion ? It seems to work if I use a template in the 'environment' directive, but this is not what I want.
Thanks for your help.
Edit: I see this same problem answered here. The proposed solution would be to use a controller instead of a directive. The reason I wanted to use a directive is the possibility to use 'require' in the inner directives, thing that can't be done with ngController I think.
By introducing external templates, I managed to find a working solution to your problem.
I'm quite certain the way you have it set up has worked at some point but I can't be certain about when. The last time I built a directive not reliant on an external markup file, I don't even know.
In any case, the following should work, if you are willing to introduce separate templates for your directives:
app.directive('environment', function () {
return {
restrict: 'A',
templateUrl: 'env.html',
replace: true,
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
app.directive('display1', function () {
return {
restrict: 'A',
scope: {
data: '='
},
templateUrl: 'display1.html',
replace: false,
link: function(scope) {
// console.log(scope.data);
}
};
});
And then for your markup (these wouldn't sit in <script> tags realistically, you would more than likely have an external template but this is simply taken from the fiddle I set up).
<script type="text/ng-template" id="display1.html">
<span>Display1 is: {{data}}</span>
</script>
<script type="text/ng-template" id="env.html">
<div>
<h1>env.value is: {{env.value}}</h1>
<span display1 data="env.value"></span>
</div>
</script>
<div>
<div environment></div>
</div>
Fiddle link: http://jsfiddle.net/ADukg/5421/
Edit: After reading that you do not want to use templates (should've done that first..), here's another solution to get it working. Unfortunately, the only one you can go with (aside from a few others, link coming below) and in my opinion it is not a good looking one...
app.directive('environment', function () {
return {
restrict: 'A',
template: function (element, attrs) {
return element.html();
},
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
And the markup:
<div environment> {{env.value}} </div>
Fiddle: http://jsfiddle.net/7K6KK/1/
Say what you will about it, but it does do the trick.
Here's a thread off of the Angular Github Repo, outlining your issue and why it is not 'supported'.
I did a small edit to your Plunker
When you create a variable on scope of directive other directives can access it two ways (presented in plunker) either directly or by two-way data binding
HTML:
<body ng-app="test">
<div environment>
{{ env.value }}
<div display1 data="env"></div>
<div display2 data="env"></div>
</div>
</body>
<input type="text" ng-model="env.value"> #added to show two-way data binding work
<div display1 info="env"></div> #changed name of attribute where variable is passed, it's then displayed inside directive template
<div display2>{{env.value}}</div> #env.value comes from environment directive not from display2
</div>
JS
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: true, #changed from {} to true, each environment directive will have isolated scope
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
template: '<span ng-bind="info.value"></span>', #added template for directive which uses passed variable, NOTE: dot in ng-bind, if you try a two-way databinding and you don't have a dot you are doing something wrong (Misko Hevry words)
scope: {
info: '=' #set two-way data binding for variable from environment directive passed in 'info' attribute
}, #removed unnecessary watch for variable
};
})
.directive("display2", function() {
return {/* ... */};
});

Single angular js directive and many controllers

I want to know how to invoke a function residing in controller on the ng-click event of template element. I have to use this directive in many pages.Hence I need to handle the click event in respective controller pages.The below code invokes the click function (moreitemdetails) residing within the directive.I tried setting the scope as moreitemdetails: '=' . It is also not working.
I have been using the directive
app.directive('groceryList', function){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};
});
The call for directive is
<grocery-list array="items"></grocery-list>
This is the template file
<div ng-click="listItemClick($event)">
<div>
<div class="item">
<span class="item-details">
{{array[0].Item}}
</span>
<span class="down-arrow"></span>
</div>
<div class="next-items">
<ul>
<li class="item" ng-repeat="list in array">
<div class="item-details" ng-click="moreitemdetails(list.Name,list.Type)">{{list.Item}}</div>
</li>
</ul>
</div>
Is there a way to get around?
I also would like to know the use of $location within another directive. Quoting the previous example (everythin is same except the directive definition and action in moreitemdetails() )
app.ui.directive('groceryList', ['$location', function(location){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
$location.path('/home/');
}
}
};
}]);
Thanks in advance
So by declaring
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
you are creating an isolated scope for your directive. One solution might be to not declare this scope in your directive. This would mean that your ng-click="moreitemdetails(list.Name,list.Type) would trigger the function on your controllers scope.
Alternatively you could use an emit and listener. To do this, in your directive you could call:
scope.moreitemdetails=function(name,type){
var deets = {
name: name,
type: type
};
scope.emit('moreitemdetails',deets)
}
Then in your various controllers you implement:
scope.$on('moreitemdetails',function(event,details){
// do some code with your name and type
}
I'm not sure exactly what you would like to know about $location, if you could be a bit more specific I might be able to help a more.
Hope this helps in some way!
EDIT:
The directive without any scope declared would look like this:
return {
restrict: 'E',
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};

How to extract angular raw code from argument in directive and render it

Not sure, how to describe this question more. So there is simple code and jsfiddle
html
<div>
<span format="the value is: {{value||'no-val'}}" value="100" my-test></span>
</div>
and javascript
App.directive('myTest', function() {
return {
restrict: 'A',
replace: true,
scope: {
format: '#'
},
template: "<span>{{format}}</span>",
link: function($scope, element, attrs) {
$scope.value = attrs.value
}
}
})
http://jsfiddle.net/VhvEy/2
My expectation <span>the value is: 100</span>
Reality <span>the value is: no-val</span>
Thanks for explanation!
When you are interpolating value it needs to refer to a scoped property in a controller.
Here is a working fiddle.
So, you need to add a controller:
App.controller('Ctrl', function($scope) {
$scope.value = 100;
})
and wire up the controller with ng-controller:
<div ng-controller="Ctrl">
The value attribute on the <span> will not automatically be bound to the angular scope.
EDIT:
If you really want to interpolate the template in the directive, you can override the compile function inside the directive:
App.directive('myTest', function($compile) {
return {
restrict: 'A',
scope: {
value: '#'
},
compile:function(element, attrs) {
var strTemplate = "<span>{{" + attrs.format +"}}</span>";
element.replaceWith(strTemplate);
}
}
})
To do this, don't interpolate in the html, just send through the text that you want to interpolate in the directive:
<span format="value || 'no-val'" value="100" my-test></span>
Here is an adapted working fiddle that shows this in action.

ng-repeat wont work in directive template

I am trying to use ng-repeat in directive template like this:
JS
var module = angular.module("myModule", []);
module.directive('direct', function ()
{
return {
restrict: 'E',
controller: "directController",
// This HTML will replace the direct directive.
replace: true,
transclude: true,
scope: {title: "#"},
template:
'<div>'
// print: this is a title, which is correct
+ '{{title}}'
+ '<br>'
// print: ["1","2","3"], which is correct
+ '{{list}}'
+ '<br>'
// doesn't print anything, why ?!
+ '<div ng-repeat="l in list">{{l}}</div>'
+ '</div>',
link: function (scope, element, attrs, controller)
{
// scope has list in it
console.log(scope);
}
}
});
module.controller('directController', ["$scope", function ($scope)
{
$scope.list = ["1", "2", "3"];
}]);
angular.bootstrap($('html'),['myModule']);
HTML
<direct title="this is a title"></direct>
result HTML
<div>
this is a title
<br>
["1","2","3"]
<br>
</div>
As shown above, ng-repeat doesn't print 'list',
while printing list or logging it, it works fine, why :( ??
Update:
The problem was using the angular bootstrap function to register the module, since I didn't use the ng-app="myModule" directive.
If I use the ng-app="myModule" directive or inject to the var module = angular.module("ng"); module it should do the trick.
Be sure to wrap the angular bootstrap in the document ready function.
$(document).ready(function() {
angular.bootstrap($('html'),['myModule']);
});
Checkout here: http://jsfiddle.net/CRWAv/
The Documention says that you have to do that on manual bootstrap:
angular.element(document).ready(function() {
angular.bootstrap(document, ['myModule']);
});

How do I store angular directive in a scope variable?

I am implementing a form builder in AngularJS and need to insert and reorder directives at runtime.
Don't even know where to start looking - all examples seem to only demonstrate static tree of directives. Two options to achieve dynamic behaviour are: a) compiling and inserting templates on the fly and b) using huge ng-switch of all possible directives. Both ways are ugly.
Can anyone suggest a better implementation?
Below is JS and html code for how I think formbuilder should look in an ideal world, please help me fill in 3 instances of TODO.
JSFiddle JavaScript:
angular.module('components', [])
.directive('checkbox', function() {
return {
restrict: 'E',
template: '<div class=f><input type=checkbox>{{name}}</input></div>'
};
})
.directive('textfield', function() {
return {
restrict: 'E',
template: '<div class=f><input type=text placeholder="{{name}}"></input></div>'
};
})
function FormBuilder($scope, $locale) {
$scope.title = 'test form';
$scope.fields = [];
$scope.add_checkbox = function() {
console.log('adding checkbox');
var field = null; // TODO: how do I instantiate a directive?
$scope.fields.push(field);
};
$scope.add_textfield = function() {
console.log('adding textfield');
var field = null; // TODO: how do I instantiate a directive?
$scope.fields.push(field);
};
}
HTML:
<div ng-app=components ng-controller=FormBuilder>
<button ng:click="add_checkbox()">new checbox</button>
<button ng:click="add_textfield()">new text field</button>
<h3>{{ title }}</h3>
<checkbox></checkbox>
<textfield></textfield>
<div ng:repeat="field in fields">
<!-- TODO field.get_html() - how? -->
</div>
</div>
I think you have a couple ways to do this as you mentioned and since you don't want to do a switch you can create a template file for each directive. ie checkbox.html, textfield.html and put the directive in each one. Then populate your fields array with ['checkbox.html', 'textarea.html'] when you add in your loop you just simply <div ng-include='field'></div>
Here is a demo: http://plnkr.co/edit/w6n6xpng6rP5WJHDlJ3Y?p=preview
You could also create another directive where you pass in the input type and have it inject it into the template. Here is a demo of this which allows you to avoid having to declare templates and letting a directive create them based on the field type:
http://plnkr.co/jhWGuMXZTuSpz8otsVRY
<div ng:repeat="field in fields">
<master-field type='field'></master-field>
</div>
This master-field directive just compiles a template based on the value of field.
.directive('masterField', function($compile) {
return {
restrict: 'E',
replace:true,
transclude: true,
scope:{
type:'='
},
template: '<div></div>',
controller: function ( $scope, $element, $attrs ) {},
link: function(scope, element, attrs) {
element.append( $compile('<' + scope.type+ '/></' +scope.type + '>')(scope) );
}
};
})

Resources