AngularJS Directive with Scope data array - javascript

i've a array. Every item of the array holds data for an directive. The array is created inside a controller.
The Model
$scope.myDirectiveData =
[ { title: "First title", content: "First content" },
{ title: "Second title", content: "Second content" } ];
The Directive Template
<div class="MyDirective">
<h1>{{myDirectiveData.title}}</h1>
<p>{{myDirectiveData.content}}</p>
</div>
How should i implement the the directive to create a item for any item in the array ? Something like...
<MyDirective data-ng-repeat="item in myDirectiveData"></MyDirective>

Here is an example using a directive. It used ng-repeat to call your directive for each object in the array on your scope. In this fiddle is this what you are looking for.
http://jsfiddle.net/gLRxc/4/
angular.module('test', [])
.directive('myDirective', function () {
var template = '<div class="MyDirective">' +
'<h1>{{ data.title }}</h1>' +
'<p>{{ data.content }}</p>' +
'</div>';
return {
template: template,
scope: {
data: '='
},
link: function (scope, element, attrs) {
}
};
})
.controller('TestController', function ($scope) {
$scope.myDirectiveData = [
{ title: "First title", content: "First content" },
{ title: "Second title", content: "Second content" }
];
})
;
Html Edited: the ng-repeat is on the same line as the directive like your question states.
<div ng-app="test" ng-controller="TestController">
<div my-directive data="item" ng-repeat="item in myDirectiveData"></div>
</div>

<div ng-repeat="item in myDirectiveData">
<h1>{{item.title}}</h1>
<p>{{item.content}}</p>
</div>

Related

Nested directives not inheriting parent scope properly

Having a problem with nested directives and inheriting the scope from a parent. It happens that the scope I want to inherit from is an isolated scope. I've the code below
test.html
<div ng-controller="testCtrl as test">
<special-select data-selected-item="test.selectedThing">
<special-select-selected-item></special-select-selected-item>
</special-select>
</div>
app.js
angular
.module('testApp', ['special-inputs'])
.controller('testCtrl', [
function() {
this.items = [
{ id: 1, name: 'alex 1', displayName:"alex 1 dn", imageUrl: 'http://placehold.it/505x100' }
];
this.selectedThing = this.items[0];
this.test = function() {
console.log('test fn');
}
}
]);
special-inputs.js
angular
.module('special-inputs', [])
.directive('specialSelect', [
function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="container" ng-transclude></div>',
scope: {
selectedItem: '='
},
link: {
pre: function(scope) {
console.log('parent link pre - ', scope.selectedItem);
},
post: function(scope) {
console.log('parent link post - ', scope.selectedItem);
}
}
}
}
])
.directive('specialSelectSelectedItem', [
function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="selected" ng-transclude></div>',
scope: true,
link: {
pre: function(scope) {
console.log('child link pre - ', scope.selectedItem);
},
post: function(scope) {
console.log('child link post - ', scope.selectedItem);
}
}
}
}
])
As you can see the nested directive is trying to create it's own isolate scope but grab the "selectedItem" from the isolate scope of the parent.
The docs say it should inherit and transclusion is semi prototypical, so I'm wondering why it's not working. Thanks. Any help would be appreciated
UPDATE
I've updated the code to show the problem I'm having now. It seems that the scope item is set in the pre + post link functions on the parent, but never get put down to the child. The console outputs
2015-12-29 15:13:44.258 special-select.js:35 parent link pre - Object {id: 1, name: "alex 1", displayName: "alex 1 dn", imageUrl: "http://placehold.it/505x100"}
2015-12-29 15:13:44.258 special-select.js:64 child link pre - undefined
2015-12-29 15:13:44.260 special-select.js:67 child link post - undefined
2015-12-29 15:13:44.260 special-select.js:38 parent link post - Object {id: 1, name: "alex 1", displayName: "alex 1 dn", imageUrl: "http://placehold.it/505x100"}
I've tried using scope.$watch and attrs.$observe to populate the valu

How to implement here hiding and displaying items in the list?

I found a code that outputs a multi-level list into Angular here
http://jsfiddle.net/c4Kp8/
var items = [{
title: 'Something',
children: [
{ title: 'Hello World' },
{ title: 'Hello Overflow' },
{ title: 'John Doe', children: [
{ title: 'Amazing title' },
{ title: 'Google it' },
{ title: 'Im a child', children: [
{ title: 'Another ' },
{ title: 'He\'s my brother' },
{ title: 'She\'s my mother.', children: [
{title: 'You never know if im going to have children'}
]}
]}
]}
]
}];
var app = angular.module('app', []);
app.controller('test', function( $scope ) {
$scope.items = items;
});
app.directive('nestedItem', ['$compile', function($compile){
return {
restrict: 'A',
link: function(scope, element){
console.log(element);
if (scope.item.children){
var html = $compile('<ul><li nested-item ng-repeat="item in item.children">{{item.title}}</li></ul>')(scope);
element.append(html);
}
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="test">
<ul>
<li nested-item ng-repeat="item in items">{{item.title}}</li>
</ul>
</div>
How to implement here hiding and displaying items in the list? Only Angular not jquery....
Hope you know that, you can make nested ng-repeat without special directive for it :-) ?
You can use for example angular-ui.bootstrap.collapse, or some custom solutions.
Solutions:
Angular UI Bootrap
Googled custom solution
Use ng-show / ng-hide / ng-if
https://docs.angularjs.org/api/ng/directive/ngShow
https://docs.angularjs.org/api/ng/directive/ngHide
https://docs.angularjs.org/api/ng/directive/ngIf
Maybe a treeview is more adequate than multiples imbricated ng-repeat
http://ngmodules.org/modules/angular.treeview

Why doesn't directive work if I put name as an attribute rather than a tag?

In the example from the Angular documentation, a directive can be used by putting its name as an attribute in a <div>. The example given is
<div ng-controller="Controller">
<div my-customer></div>
</div>
with the js looking like
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
However, in a similar example, over here, if I change the html code from <tree> to <div tree>, the code no longer works.
Why not?
The code from JS Fiddle:
<div ng-app="myapp">
<div ng-controller="TreeCtrl">
<tree family="treeFamily">
<p>{{ family.name }}</p>
</tree>
</div>
</div>
var module = angular.module('myapp', []);
module.controller("TreeCtrl", function($scope) {
$scope.treeFamily = {
name : "Parent",
children: [{
name : "Child1",
children: [{
name : "Grandchild1",
children: []
},{
name : "Grandchild2",
children: []
},{
name : "Grandchild3",
children: []
}]
}, {
name: "Child2",
children: []
}]
};
});
module.directive("tree", function($compile) {
return {
restrict: "E",
transclude: true,
scope: {family: '='},
template:
'<ul>' +
'<li ng-transclude></li>' +
'<li ng-repeat="child in family.children">' +
'<tree family="child">{{family.name}}</tree>' +
'</li>' +
'</ul>',
compile: function(tElement, tAttr, transclude) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function(clone, scope) {
iElement.append(clone);
});
};
}
};
});
tree {
margin-left: 20px;
display: block;
}
The restrict option is used to specify how a directive can be invoked on the page. There are four different ways to invoke a directive, so there are four valid options for restrict:
'A' - attribute - <span ng-sparkline></span>
'E' - element - <ng-sparkline></ng-sparkline>
'C' - class - <span class="ng-sparkline"></span>
'M' - comment - <!-- directive: ng-sparkline -->
In your case its not working because it is defined as restrict 'E' - element.
It's because of the restrict option in the directive.
Here it is set to e which means to match only the element name.
More https://docs.angularjs.org/guide/directive

Acessing directive's scope within trancluded ng-repeat

I'm writing a component and need to ng-repeat on directive's isolate scope property.
Consider this:
<div some-directive>
<h2>Let's repeat something</h2>
<p ng-repeat="item in contr.items">
Title: {{ item.title }}
Description: {{ item.desc }}
</p>
</div>
My idea is that directive provides a collection of items (a resolve in real code, but I will keep it simple here). Right now I have:
.directive('someDirective', function() {
return {
scope: {},
transclude: true,
template: '<div ng-transclude></div>',
controller: function($scope) {
$scope.items = this.items = [{
title: "Item 1 title",
desc: "Description"
}, {
title: "Item 2 title",
desc: "Another desc"
}, {
title: "Item 3 title",
desc: "Third desc"
}, {
title: "Item 4 title",
desc: "Third desc"
}];
},
controllerAs: 'contr'
};
});
See this Plunkr (and edit if you like).
Do you have any ideas?
It's by design that content added via ng-transclude will be binded with an outer (original) scope, not a scope of the current element that ng-transclude is on.
If you want the transcluded content to be binded with the isolate scope, you could use a modified version of ng-tranclude like this:
.directive('myTransclude', function () {
return {
restrict: 'EAC',
link: function(scope, element, attrs, controllers, transcludeFn) {
transcludeFn(scope, function(nodes) {
element.empty();
element.append(nodes);
});
}
};
});
and use it instead of ng-tranclude like this:
template: '<div my-transclude></div>',
Example Plunker: http://plnkr.co/edit/yDwuwCYtAzxyIhJRgZoJ?p=preview

How to convert this AngularJS controller in this module to another format?

I have this AngularJS controller;
angular.module('plunker', ['ui.bootstrap']);
var TabsDemoCtrl = function ($scope) {
$scope.tabs = [
{ title:"Dynamic Title 1", content:"Dynamic content 1" },
{ title:"Dynamic Title 2", content:"Dynamic content 2", disabled: true }
];
$scope.alertMe = function() {
setTimeout(function() {
alert("You've selected the alert tab!");
});
};
$scope.navType = 'pills';
};
I want to convert it to another controller (from AngularJS seed) to this format below;
angular.module('myApp.controllers', []).
controller('MyCtrl1', [function() {
}])
.controller('MyCtrl2', [function() {
}]);
The index.html looks like this;
<div ng-controller="TabsDemoCtrl">
<p>Select a tab by setting active binding to true:</p>
<p>
<button class="btn btn-default btn-sm" ng-click="tabs[0].active = true">Select second tab</button>
<button class="btn btn-default btn-sm" ng-click="tabs[1].active = true">Select third tab</button>
</p>
<p>
<button class="btn btn-default btn-sm" ng-click="tabs[1].disabled = ! tabs[1].disabled">Enable / Disable third tab</button>
</p>
<hr />
How should the converted controller look like?
I tried the following but it did not work.
angular.module('myApp.controllers', []).
controller('TabsDemoCtrl', [function() {
$scope.tabs = [
{ title:"Dynamic Title 1", content:"Dynamic content 1" },
{ title:"Dynamic Title 2", content:"Dynamic content 2", disabled: true }
];
$scope.alertMe = function() {
setTimeout(function() {
alert("You've selected the alert tab!");
});
};
$scope.navType = 'pills';
}])
.controller('MyCtrl2', [function() {
}]);
What did I do wrong?
you have to inject the $scope into the controller to make it work
angular.module('myApp.controllers', []).
controller('TabsDemoCtrl', ['$scope', function($scope) {
$scope.tabs = [
{ title:"Dynamic Title 1", content:"Dynamic content 1" },
{ title:"Dynamic Title 2", content:"Dynamic content 2", disabled: true }
];
$scope.alertMe = function() {
setTimeout(function() {
alert("You've selected the alert tab!");
});
};
$scope.navType = 'pills';
}]);
You might want to specify $scope in second line. So your code would be like
angular.module('myApp.controllers', []).
controller('TabsDemoCtrl', ['$scope', function() {
$scope.tabs = [
{ title:"Dynamic Title 1", content:"Dynamic content 1" },
{ title:"Dynamic Title 2", content:"Dynamic content 2", disabled: true }
];
....
Also, make sure your 'myApp.controllers' is registered. That might help.

Categories