I am trying to create a reusable html element / angular directive that will be used inside of ng-repeat so I want to pass it the values it will display in the DOM.
Something worth noting, I don't care for the values to bind. They can be a one-time binding simply displaying their values the first time ng-repeat creates them.
For example here is my directive:
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
templateUrl: '../Pages/Views/Templates/newsListing.html',
scope: {},
link: function (scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});
My HTML template:
<div>
<span class="glyphicon glyphicon-list-alt logo-green"></span>
<label>{{DateValue}}</label>
<label>{{Category}}</label>
<label class="noBorder">{{Content}}</label>
What I want the ending product to be:
<news-Listing Date="{{someValue}}" Category="{{someValue}}" Content="{{someValue}}"></news-Listing>
I have never created a directive before and all the guides I am trying to follow don't explain the scope, and the way binding happens inside of a directive.
Try like this
var app = angular.module('anApp', []);
app.controller('aCtrl', function($scope) {
$scope.data = [{date:"2000-12-12",category:"sport",content:"this is a test"}]
});
app.directive('newsListing', function () {
return {
restrict: 'AE',
replace: 'true',
template: '<div><label>{{date}}</label><p>{{category}}</p><p>{{content}}</p></div>',
scope: {
date:'#',
category:'#',
content:'#'
},
link: function (scope, elem, attrs) {
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="anApp" ng-controller="aCtrl">
<div ng-repeat="d in data">
<news-listing date="{{d.date}}" category="{{d.category}}" content="{{d.content}}" ></news-listing>
</div>
</div>
Here is a working example of what you want: https://jsfiddle.net/jonva/kuk3pbbz/
.directive('newsListing', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<div> < span class = "glyphicon glyphicon-list-alt logo-green" > < /span> < label > {{dateValue}} < /label> < label > {{category}} < /label> < label class = "noBorder" > {{content}} < /label>',
scope: {
dateValue: '#',
content: '#',
category: '#',
},
link: function(scope, elem, attrs) {
//Fairly sure this is where the binding needs to happen?
}
};
});
Related
...
...
UPDATE
HTML
<my-directive ng-repeat="item in items = ( data | filter: {isExists: true})">
something
</my-directive>
<my-second-directive counter="{{items.length}}"></my-second-directive>
JS
angular.module('directives')
.directive('myDirective', function () {
...
})
.directive('mySecondDirective', function () {
return {
restrict: 'EA',
transclude: false,
replace: true,
scope: {
counter: '#'
},
template: '',
link: function (scope, element, attrs) {
alert(scope.counter);
}
});
Excuse me I've not described my question well. My first directive should be ngRepeated, with filter. But in my second directive, I would like to allow to show a counter, how many first directive is currently instantiated, because the user will be able to add and remove instances. So I would like to get the value of the items.length by fly with the second directive. But the link() method of the second directive is fired prior the ngRepeat, so the value of the counter will be an empty string.
Thanks in advance
UPDATE 2
.directive('cardGroupHeader', function($templateCache){
return {
restrict: 'EA',
transclude: true,
replace: true,
require: '^cardGroup',
scope: {
cbiscounter: '=?',
cbcounter: '=?',
cbisarrow: '#?'
},
template: $templateCache.get('card-group-header-tpl.html'),
link: function(scope, $element, $attrs, cardGroupController) {
scope.rowId = cardGroupController.getCurrentId();
console.log(scope.cbcounter);
scope.toggle = function () {
cardGroupController.toggle(scope.rowId)
}
angular.element(document).ready(function () {
console.log(scope.cbcounter);
});
scope.$watch('scope.cbcounter', function (n, o) {
if(n && n != o) {
console.log(n);
}
});
//scope.cbcounter++;
}
};
})
HTML
<card-group-header cbiscounter="true" cbarrow="true" cbcounter="data.length">Waiting for Approval</card-group-header>
<card-group-row cbCollapsed="false">
<card ng-repeat="approveItem in data = (approveItems | filter: {isApproved: false, isRejected: false})">
TEMPLATE
$templateCache.put('card-group-header-tpl.html', '<div class="card-group-header" ng-click="toggle()"><span ng-transclude></span><span class="card-group-counter" ng-if="cbiscounter">{{cbcounter}}</span></div>');
When I change the data.length to 2, this is transferred well. If I use the data.length the scope.cbcounter is always undefined. In case of 2 I've got it back on the console.log(scope.cbcounter);
The counter: '#' means that you are accepting a string value. If you wanted to pass an expression you could either use:
<my-second-directive counter="{{ items.length }}"></my-second-directive>
Or:
.directive('mySecondDirective', function () {
return {
restrict: 'EA',
transclude: false,
replace: true,
scope: {
counter: '=' // Accept two ways binding
},
template: '',
link: function (scope, element, attrs) {
alert(scope.counter);
}
});
EDIT:
I finally quite understand the problem! It's because of attributes are not interpolated until after the link phase. You have two following options:
The first option is wrapping every in the link inside $timeout to take it away from the event loop and be executed after DOM finished manipulating:
.directive('mySecondDirective', function ($timeout) {
return {
restrict: 'EA',
transclude: false,
replace: true,
scope: {
counter: '=' // Accept two ways binding
},
template: '',
link: function (scope, element, attrs) {
$timeout(function() {
alert(scope.counter);
});
}
});
Secondly, using $observe:
attrs.$observe('counter', function(value){
console.log(value);
});
or using $watch as #jusopi suggested.
I think this would be what you want.
Html
<div ng-app="testapp" ng-controller="testctrl">
<div ng-repeat="item in filtereditems">
{{item}}
</div>
<testdir counter="filtereditems.length" />
</div>
Javascript
angular.module('testapp', [])
.directive('testdir', function(){
return {
restrict: 'E',
scope:{
counter: '='
},
link: function(scope, element, attrs) {
alert(scope.counter);
}
}
})
.controller('testctrl', function($scope, $filter){
$scope.items = [
{name: 'A', isExists: true},
{name: 'B', isExists: false},
{name: 'C', isExists: true},
{name: 'D', isExists: false}
];
$scope.filtereditems = $filter('filter')($scope.items, {isExists: true});
})
My jsfiddle is here.
In addition to #LVarayut's answer about the scope's binding expression, the reason the alert is undefined is because linking is not part of the $digest cycle. So binding and data hasn't be effected yet (don't quote me on that, it's the best way I could verbalize what I'm showing in the code below).
Instead you need to use a watcher to trigger the alert
link: ($scope, elem, attrs)->
#undefined because linking isn't part of the $digest cycle
#alert $scope.count
$scope.$watch 'count', (n, o)->
if n and n isnt o
true
#alert n
http://plnkr.co/edit/xt95gb3cTXfUEHgpWK1W?p=preview
I have a directive, I want that template to show all the rows from data set
app.directive('exampleDirective', [ 'TestProvider', 'TestFactory', function (TestProvider, TestFactory) {
return {
restrict: 'E',
template: '<tr><td>{{Name}} </td><td>{{Surname}}</td></tr>',
replace: true,
link: function(scope, elm, attrs) {
var dat= TestFactory.dataReturn();
for (var i = 0; i < dat.length; i++) {
scope.Name = dat[i].Name;
scope.Surname = dat[i].Surname;
console.log(dat[i]);
}
// alert("hah");
}
};
}]);
How can I make it repeat like ng-repeat ?
Assuming your service returning a promise. Here is the simple code to repeat your data in table.
app.directive('exampleDirective', [ 'TestProvider', 'TestFactory', function (TestProvider, TestFactory) {
return {
restrict: 'E',
template: '<table ng-repeat="person in persons"><tr><td>{{person.Name }} </td><td>{{person.Surname}}</td></tr></table>',
replace: true,
link: function(scope, elm, attrs) {
TestFactory.dataReturn().then(function(resp){
scope.persons = resp.data;
});
}
};
}]);
Alternatively, you could redefine your element directive to represent a single object, and then ng-repeat the directive itself. Obviously, this would require moving the location where you use the factory. Your application architecture may or may not be able to accommodate this change.
directive:
app.directive('exampleDirective', [function () {
return {
restrict: 'E',
template: '<tr><td>{{person.Name}} </td><td>{{person.Surname}}</td></tr>',
replace: true,
scope: {
person: '='
}
};
}]);
And usage:
<example-directive ng-repeat="data in dataFromFactory" person="data"></example-directive>
Here's my fiddle
I basically want to be able to change the text when a button is pressed. I have tried with both $observe and $watch inside link, but I still don't manage to get it working.
Code:
(function(){
angular.module('app', [])
.directive('testDirective', function(){
return {
restrict: 'E',
scope: {
title: '#'
},
template: '<div>this is a {{ title }}</div>',
link: function(scope, element, attrs) {
//?
}
};
});
})()
You need to pass data as scope variable, you should not pass it as a string if you want to track changes.
check this fiddle, replace counter data with your desired data. Hope this helps
<div ng-controller='myctrl'>
<test-directive title="counter"></test-directive>
<hr></hr>
<button type="button" ng-click = 'onclickbutton()'>Change names</button>
</div>
(function(){
angular.module('app', [])
.controller('myctrl',function($scope){
$scope.counter = 0;
$scope.onclickbutton = function(){
$scope.counter++;
}
})
.directive('testDirective', function(){
return {
restrict: 'E',
scope: {
title: '='
},
template: '<div>this is a {{ title }}</div>',
link: function(scope, element, attrs) {
}
};
});
})();
I have just started using AngularJS. Is there a way to use a directive from an another directive ?
I would like to add new <div time></div> on click on the parent.
EDIT : index.html
<div class="time" addtime ng-click="addTime()">
<!-- ADD NEW <div resizable draggable ...></div> from directive 'time' HERE -->
</div>
EDIT : directive.js
dragDirectives.directive('addtime', function() {
return {
restrict: 'A',
link: function(scope, element, attr, ctrl) {
},
controller: function addtimeCtrl($scope, $element){
$scope.addTime = function() {
// ADD NEW <div resizable draggable ...></div> from directive 'time'
};
}
}
});
dragDirectives.directive('time', function() {
return {
restrict: 'A',
template: '<div resizable draggable class="duree" ng-class="{\'resize-active\': isResizeActive, \'drag-active\': isDragActive }" ng-style="{ width: myWidth, left: myLeft }">',
link: function(scope, element, attr) {
}
}
});
With Angular you do not do DOM-related or jQuery-ish stuff (i.e. addEventListener). The approach would be:
Have a model describe the <div time> elements you have, e.g.:
scope.timeElements = [];
Itarate over it in the template:
<div time ng-repeat="t in timeElements"></div>
Handle the event by manipulating the model, e.g.:
(HTML)
<div class="time" addtime ng-click="addTime()">
(JS)
scope.addTime = function() {
scope.timeElements.push(something);
};
This is only an outline for a real solution, as many details are still missing, but you should get the idea. A fiddle would help getting more specific.
Also check out this.
require: '^parent' // a better way to go
dragDirectives.directive('addtime', function() {
return {
restrict: 'A',
template: '<div time ng-repeat="t in timeElements"></div>',
link: function(scope, element, attr, ctrl) {
scope.timeElements = [1, 2, 3]
},
controller: function addtimeCtrl($scope) {
this.addTime = function() {
$scope.timeElements.push($scope.timeElements.length + 1);
};
}
}
});
dragDirectives.directive('time', function() {
return {
restrict: 'A',
require: '^addtime',
template: '<div ng-click="addTime()">time</div>',
link: function(scope, element, attr, ctrl) {
scope.addTime = ctrl.addTime;
}
}
});
This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}