This question already has answers here:
Recursion in Angular directives
(9 answers)
Closed 7 years ago.
Edit: Just came across this SO post. It best answers my questions.
How would you implement the following recursively/better:
HTML
<div ng-controller='MainController as vm'>
<div ng-repeat='tango in vm.tangos'>
<p>{{ tango.text }}</p>
<div ng-repeat='subtango in tango.children'>
<p>{{ subtango.text }}</p>
<div ng-repeat='subsubtango in subtango.children'>
<p>{{ subsubtango.text }}</p>
<!-- ... -->
</div>
</div>
</div>
</div>
JS
angular
.module('app', [])
.controller('MainController', MainController)
;
function MainController() {
var vm = this;
vm.foo = 'bar';
vm.tangos = [{
text: '1',
children: [
{
text: '1a',
children: []
},
{
text: '1b',
children: [
{
text: '1bi',
children: []
}
]
}
]
}];
}
Using a directive doesn't seem to work (plnkr):
HTML
<div ng-controller='MainController as vm'>
<div ng-repeat='tango in vm.tangos'>
<tango tango='tango'></tango>
</div>
</div>
tango.html
<p>{{ tango.text }}</p>
<div ng-repeat='tango in tango.children'>
<tango tango='tango'></tango>
</div>
directive
function tango() {
return {
restrict: 'E',
scope: {
tango: '&'
},
templateUrl: './tango.html'
};
}
I assume that it's instantiating(probably not the right word) the directive even when tango.children is empty. If so, I'm not sure how to prevent that.
Check this demo: Plunker.
Define a template and use $compile to do the trick:
.directive('item', ['$compile', function( $compile ) {
var itemTemplate =
'<div>{{ text }} # of children: {{children.length}}</p>' +
'<div ng-show="children.length > 0">' +
'<div ng-repeat="i in children">' +
'<div item children="i.children" text="{{i.text}}">{{ i | json }}</div>' +
'</div>' +
'</div>' +
'</div>';
return {
restrict: 'AE',
scope: {
children: '=',
text: '#'
},
// template: itemTemplate
link: function (scope, element) {
element.html('').append( $compile( itemTemplate )( scope ) );
}
}
}]);
Related
I want's to get the value of ng-model within the the ng-repeat on my own directive and use the value of that ng-model in the directive for a fuction.
here is a plunker to see code in action :
https://plnkr.co/edit/MaI8DOtdc4yucXgNo05p?p=preview
here is my directive code :
(function() {
'use strict';
angular.module('barname.rtEditor', [])
.directive('rtEditor', rtEditor);
function rtEditor() {
return {
restrict: 'E',
replace: true,
templateUrl: 'rt-ht.html',
compile: compile
};
function compile() {
return {
post: function(scope, element, attrs) {
scope.com = {
inp: [{
name: 'video',
model: scope.vimodel,
command: 'insertHTML',
modelcommand: scope.vimodel
}, {
name: 'pic',
model: scope.pimodel,
command: 'insertImage',
modelcommand: scope.pimodel
}, {
name: 'link',
model: scope.linkmodel,
command: 'createLink',
modelcommand: scope.linkmodel
}]
};
scope.$watch('vimodel', function(newValue, oldValue) {
console.log('vim model watch :-> ', newValue);
});
scope.$watch('texmodel', function(newValue, oldValue) {
console.log('text model watch :-> ', newValue);
});
scope.execIn = function(cmd, val) {
console.log('cmd val :->', cmd + ' | ' + val);
scope.vimodel = '';
scope.texmodel = '';
};
}
};
}
}
})();
and here is the html code :
<div>
<div ng-repeat="inp in com.inp" id="{{inp.name}}">
<label>{{inp.name}} :</label>
<input type="text" ng-model="inp.model" />
<span ng-click="execIn(inp.command, inp.modelcommand)">doIt!</span>
</div>
<br>
<hr>
<br>
<div>
<label>widout ng repeat:</label>
<input type="text" ng-model="texmodel" />
<span ng-click="execIn('textIn', texmodel)">doIt!</span>
</div>
</div>
I can easily get the value of the ng-model without ng-repeat
I am working with angular 1.5.6 component. I am trying to use the output binding ('&') but impossible to get it work. I have plunkered my issue.
Code for index.html :
<body ng-app="MyApp">
<my-view></my-view>
</body>
Code for the component for which I want to use output binding :
app.component('myInput', {
template: [
'<div class="form-group">',
'<label>{{$ctrl.label}}</label>',
'<input placeholder="{{$ctrl.placeholder}}" class="form-control" ng-model="$ctrl.fieldValue"/>',
'</div>'
].join(''),
controller: function() {},
bindings: {
label: '#',
placeholder: '#',
fieldValue: '=',
onUpdate: '&'
}
});
Code for the parent component (output binding is done with the attribute on-update):
app.component('myView', {
template: [
'<div class="container">',
' <h2>My form with component</h2>',
' <form role="form">',
' <my-input on-update="$ctrl.updateParam()" label="Firstname" placeholder="Enter first name" field-value=$ctrl.intermediaryData.firstName ></my-input>',
' </form>'
].join(''),
controller: function() {
var ctrl = this;
ctrl.userData = {
firstName: 'Xavier',
lastName: 'Dupont',
age: 25
};
ctrl.intermediaryData = {
firstName: ctrl.userData.firstName,
lastName: ctrl.userData.lastName,
age: 25
};
function updateParam(){
console.log("I have updated the component");
}
}
});
My bad, I have forgotten to put ng-change in input component. I managed to solve the issue like this :
app.component('myInput', {
template: [
'<div class="form-group">',
'<label>{{$ctrl.label}}</label>',
'<input ng-change="$ctrl.change()" placeholder="{{$ctrl.placeholder}}" class="form-control" ng-model="$ctrl.fieldValue"/>',
'</div>'
].join(''),
controller: function() {
var ctrl = this;
ctrl.change = function(){
ctrl.onUpdate();
}
},
bindings: {
label: '#',
placeholder: '#',
parentParam: '#',
fieldValue: '=',
onUpdate: '&'
}
});
I am new to angularjs. I want to dynamically add directives to a div based the titles from controller.. Below is what i tried...
Below is the directives and factory i used
QtTemplate.directive("shortanswer",function()
{
return{
restrict:"A",
templateUrl:'assets/directives/shortAnswer.html'
}
})
Template.factory("questionList",function()
{
var questionList={};
questionList.testid="1";
questionList.name="Maths";
questionList.Questions =
[
{"title":"truefalse"},
{"title":"shortanswer"}
]
return questionList;
})
Template.controller("questionControl",['$scope','questionList',function($scope,questionList)
{
$scope.name = questionList.name;
$scope.id = questionList.testid;
$scope.Questions = questionList.Questions;
}])
Template.directive('dynamicDirective',function($compile)
{
return {
restrict: 'A',scope:{},
replace: false,
terminal: true,
priority: 1000,
link:function(scope,element,attrs)
{
element.attr(scope.$eval(attrs.dynamicDirective),"");
element.removeAttr("dynamic-directive"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-dynamic-directive");
$compile(element)(scope);
}
};
});
QtTemplate.directive("shortanswer",function()
{
return{
restrict:"A",
templateUrl:'assets/directives/shortAnswer.html'
}
})
Below is how i use ng-repeat
<div ng-repeat="Questions in Questions" >
<div dynamicDirective="{{Questions.title}}" ></div>
</div>
This is not working
use-kebab-case-in-html
<div ng-repeat="Question in Questions" >
<div dynamic-directive="{{Question.title}}" ></div>
</div>
You need to write dynamic-directive instead of dynamicDirective in your HTML. Camelcase in Javascript needs to be written as snakecase kebab-case in HTML.
Here you are:
<div ng-repeat="Question in Questions" >
<div dynamicDirective="{{Question.title}}" ></div>
</div>
Hope this helps.
Here is a custom radio buttons:
The angular HTML code:
<div ng-app="app">
<div ng-controller="ctrl" class="sm-box">
<p>Please select a mode:
<sm-radio model="mode" value="MODE1" label="One" ng-click='mode="MODE1"'></sm-radio>
<sm-radio model="mode" value="MODE2" label="Two"></sm-radio>
<sm-radio model="mode" value="MODE3" label="Three"></sm-radio></p>
<p>Selected mode is: {{mode}}</p>
</div>
</div>
The corresponding JavaScript:
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.mode = 'MODE1';
}]);
app.directive('smRadio', ['$compile', function($compile) {
return {
restrict: 'E',
template: function( tElt, tAttrs ) {
tElt.addClass( 'sm-radio' );
tElt.append(
'<input type="radio"' +
' name="' + tAttrs.model + '"' +
' ng-model="' + tAttrs.model + '"' +
' value="' + tAttrs.value + '"' +
' /><label>' + tAttrs.label + '</label>'
);
if( ! tElt.attr('ng-click')) {
// This is a first try
tElt.attr('ng-click', 'mode="' + tAttrs.value + '"' );
// this is a variant
tElt.ngClick = 'mode="' + tAttrs.value + '"';
// None has effect...
$compile(tElt); // compilation here had twice elements!!!
}
}
};
}]);
The problem is "One" is clickable and sets the model "mode" to "MODE1" but "Two" and "Three" are not, only the radio buttons may select "MODE2" or "MODE3".
As you can see in the HTML source, the first have "ng-click" attribute when the other have not. I'm trying to add "ng-click" attribute in my directive, by code but I'm unsuccessful.
How can I add "ng-click" attribute in a directive?
EDIT: Answers guide to not use ng-click at all, so title has been updated
It is considered best practice to pass values to directive from "outside world" by using isolated scope. As you can see, it makes it easier too:
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.mode = 'MODE1';
}]);
app.directive('smRadio', function($compile) {
return {
restrict: 'E',
scope: {
model: '=',
value: '#',
label: '#'
},
template: '' +
'<input type="radio" name="name" ng-model="model" value="value"' +
' ng-checked="model==value" ng-click="model=value">' +
'<label ng-click="model=value">{{label}}</label>',
compile: function(tElt) {
tElt.addClass( 'sm-radio' );
}
};
});
Here's working example: http://jsbin.com/fejori/3/edit?js,output
(In case of this much code in 'template' attribute of a directive, I'd move it to outside html file and link it instead by using templateUrl attribute)
Here's doing it without ng-click. fiddle: http://jsfiddle.net/HB7LU/14244/
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.modes = [
{label: 'MODE 1', value: 1 },
{label: 'MODE 2', value: 2},
{label: 'MODE 3', value: 3}
];
$scope.selected = 2;
}]);
app.directive('smRadio', function() {
return {
restrict: 'E',
scope: {model: '=', selected: '='},
template: function( tElt, tAttrs ) {
return '<label ng-repeat="radio in model"><input type="radio" ng-value="radio.value" name="modes" ng-model="selected" ng-change="change(radio.value)">{{radio.label}}</label>';
},
link: function(scope, elem, attrs){
scope.change = function(val){
scope.selected = val;
}
}
};
});
Here is exactly what I want, largely inspired by tomek answer, simplified and the idea of label in place of div of Ervald answer.
var app = angular.module('app', []);
app.controller('ctrl', ['$scope', function( $scope ) {
$scope.xmode = 'MODE2';
$scope.ymode = 'MODE_B';
}]);
app.directive('smRadio', function() {
return {
restrict: 'E',
scope : {
model: '=',
value: '#',
label: '#'
},
template:
'<label class="sm-radio"' +
' ng-model="model">' +
'<input type="radio" name="{{model}}"' +
' ng-model="model"' +
' ng-value="value"' +
' ng-checked="model==value"> {{label}}</label>'
};
});
.sm-box {
background-color: lightgray;
border : solid 1px;
padding : 4px;
width : 500px;
}
.sm-radio {
background-color: lightblue;
color : #fff;
display : inline-block;
border : 1px solid darkgray;
border-radius : 8px;
padding : 8px;
}
.sm-radio>input {
width : 24px;
height : 24px;
vertical-align: text-bottom;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" class="sm-box">
<p>Please select a Xmode:
<sm-radio model="xmode" value="MODE1" label="One"></sm-radio>
<sm-radio model="xmode" value="MODE2" label="Two"></sm-radio>
<sm-radio model="xmode" value="MODE3" label="Three"></sm-radio></p>
<p>Selected mode is: {{xmode}}</p>
<p>Please select a Ymode:
<sm-radio model="ymode" value="MODE_A" label="A"></sm-radio>
<sm-radio model="ymode" value="MODE_B" label="B"></sm-radio>
<sm-radio model="ymode" value="MODE_C" label="C"></sm-radio></p>
<p>Selected mode is: {{ymode}}</p>
</div>
</div>
A little difficult to explain,
we have worked through this sample
https://egghead.io/lessons/angularjs-understanding-isolate-scope
but its not quite matching what we are trying to achieve.
we have built a controller which simply sets myData
ClinicalNotesCtrl.controller('notesController', function notesController($scope, $http, $modal, $log) {
$scope.myData = { name: "Moroni", age: 50, result: "Sodium" };
$scope.changename = function (h) {
h.name = "Simon";
h.age = "34";
h.result = "This is a test";
}
});
Below is the directive called kid. This just simply prints out the value
Results.directive("kid", function () {
return {
restrict: "E",
// scope: { myValue: '=simon' },
// scope:{},
template: '<input type="text" ng-model="myData.name">{{myData.name}}' // '<div>{{$scope.timelineactivitydata}}</div>',
};
});
and finally this is the HTML page,
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
<kid simon="myData"></kid>
<label ng-click="changename(myData)">Change Name</label>
What we are trying to achieve is to somehow relate a particular label to a kid directive, so that when clicking on the label, only the related kid will change its name, rather than all of them.
Hope that makes sense,
As requested in the comments,
please see the plunker :-
plnkr.co/edit/3NMBNTrLT29EIFNo9lbA?p=preview
I am posting a solution here but I am not sure if it solves anything for you.
JS code
var myApp = angular.module('myApp', []);
myApp.controller('notesController', function notesController($scope, $http) {
$scope.myData = [{ name: "Moroni", age: 50, result: "Sodium" },
{ name: "Naomi", age: 50, result: "Sodium" },
{ name: "Rambo", age: 50, result: "Sodium" }
];
$scope.changename = function (h) {
h.name = "Simon";
h.age = "34";
h.result = "This is a test";
};
})
.directive("kid", function () {
return {
restrict: "E",
scope:{
myData:"=simon",
changed:"&change"
},
template: '<input type="text" ng-model="myData.name"/><span>'
+'{{myData.name}}</span><label '
+'ng-click="changed({\'data\':myData});">'
+'Change Name</label><br/>'
};
});
HTML
<div ng-app="myApp" >
<div ng-controller="notesController" >
<kid ng-repeat="data in myData" simon="data" change="changename(data)">
</kid>
</div>
</div>