Angularjs directive not redrawing - angularjs

I am trying to get a recursive directive working in angular. After quite a bit of time on stackoverflow and digging through the angular documentation I have got most of it working. I'm having a hard time getting the actions working though. The object is getting updated as I would expect, but the directive doesn't seem to be redrawn accordingly.
Here is the directive:
.directive('formgenerator', ['$compile', function ($compile) {
return {
restrict: 'E',
terminal: true,
scope: { val: '=', index: '=' },
replace: true,
link: function (scope, element, attrs) {
var template = '<div data-ng-if="val">';
template += !scope.val.type ? ''
: scope.val.type === 'text' ? '<label>{{val.label}}</label><input type="text" data-ng-model="val.value"></input><button ng-click="deleteMe(index)">delete</button>'
: scope.val.type === 'select' ? '<label>{{val.label}}</label><select data-ng-model="val.value" data-ng-options="v.name for v in val.values track by v.id"></select><button ng-click="deleteMe(index)">delete</button>'
: scope.val.type === 'multiselect' ? '<label>{{val.label}}</label><select data-ng-model="val.value" multiple="multiple" data-ng-options="v.name for v in val.values track by v.id"></select><button ng-click="deleteMe(index)">delete</button>'
: '';
template += '</div>';
if (angular.isArray(scope.val.inputs)) {
template += '<ul class="indent"><li ng-repeat="input in val.inputs track by $index"><formgenerator val="input" index="$index"></formgenerator></li></ul>';
}
scope.deleteMe = function (index) {
scope.$parent.val.inputs.splice(index, 1);
//var inpts = scope.$parent.val.inputs;
//inpts.splice(index, 1);
//scope.$parent.val.inputs = inpts;
//scope.$parent.$parent.val.inputs.splice(index, 1);
//scope.$parent.$parent.$parent.val.inputs[scope.$parent.this.index].inputs.splice(scope.$parent.this.index, 1);
};
var newElement = angular.element(template);
$compile(newElement)(scope);
element.replaceWith(newElement);
}
};
}]);
Here is the object the controller is passing into the directive:
form = {
inputs:
[
{
type: 'text',
value: 'textValue',
label: 'Text value',
defaultValue: 'defaultTextValue'
},
{
inputs:
[
{
type: 'text',
value: 'textValue1',
label: 'Text value1',
defaultValue: 'defaultTextValue1'
},
{
type: 'select',
value: 'textValue2',
values: [{ name: '1st', id: 1 }, { name: '2nd', id: 2 }],
label: 'Text value2',
defaultValue: 'defaultTextValue2'
},
{
type: 'multiselect',
value: 'textValue3',
values: [{ name: '1st', id: 1 }, { name: '2nd', id: 2 }],
label: 'Text value3',
defaultValue: 'defaultTextValue3'
}
]
}
]
};
Here is the jsFiddle: http://jsfiddle.net/5YCe7/1/
Basically, if I hit the delete button next to Text value2, I would expect the single select to 'disappear' and the multiselect to 'move up'. What seems to be happening though is that the values of the multiselect move in place of the values for the select.
Any help on this would be greatly appreciated.

There are a few errors here:
When you press deleteMe, you need to again compile and replace the element with the new element. link won't automatically be called again.
I don't think your index is being assigned correctly
I would recommend looking at a few links before making recursive directives:
Is it possible to make a Tree View with Angular?
Recursion in Angular directives

Related

Using Custom editors in grid column with Angular Kendo UI

I am trying to use custom editors for an editable kendo ui grid in my angular app.
For some reason( which I am not able to trace) the custom editor is not triggered.
I am expecting the following to be triggered but it does not work.
console.log("Editor Launched", options);
Here is the plunker for the same:
http://plnkr.co/edit/WioRbXA3LHVVRQD95nXA?p=preview
app.controller('MainCtrl', function($scope) {
$scope.model = {};
$scope.model.dataSource = new kendo.data.DataSource({
data: createRandomData(10),
schema: {
model: {
fields: {
City: { type: "string" },
Title: { type: "string" },
BirthDate: { type: "date" },
Age: { type: "number" }
}
}
},
pageSize: 16,
editable:true
});
$scope.addWWNumEditor= function (container, options) {
console.log("Editor Launched", options);
$('<input kendo-numeric-text-box k-min="10" k-max="20" style="width: 100%;" data-bind="value:' + options.field + '"/>')
.appendTo(container);
}
$scope.controlIsDisabled=function(model){
//console.log("model",(model.Age>=50));
var toReturn = (model.Age>50)?"columnDisabled" : "columnActive";
//console.log('to Return',toReturn);
return toReturn;
}
$scope.model.columns = [
{ field: 'City', title: 'City' },
{
field: 'Title',
title: 'Title',
template:'<span style="color:red;">EDITABLE</span><span ng-
class="controlIsDisabled(dataItem)">#=Title#</span>'
},
{
field: 'Age',
title: 'Age',
template:'<span ng-class="controlIsDisabled(dataItem)">#=Age#</span>'
,
editor:$scope.addWWNumEditor
}
];
});
Assuming your Plunkr mirrors your actual code, the primary problem I'm seeing is in your binding of k-columns on the grid element.
You currently have k-columns="{{model.columns}}", but the {{}} are unnecessary here. Changing to k-columns="model.columns" causes your editor function to execute as expected.

AngularJS: how to build nested divs?

How can I build the following structure using ng-repeat? Solution must support an arbitrary depth...
I've played with ng-repeat-start & ng-repeat-end but without any luck so far. Any hint is greatly appriciated
<div>1
<div>2
<div>3</div>
</div>
</div>
It can be accomplished through a directive that leverages ng-repeat to render values from nested objects. See it working here.
Directive
app.directive('divRepeater', function($compile) {
return {
restrict: 'E',
replace: true,
template: '<ul></ul>',
scope: {
obj: '='
},
link: function(scope, element) {
var el = angular.element('<span/>');
el.append('<li>' + scope.obj.id + '</li>');
if (scope.obj.nestedObjs) {
var nestedObjs = angular.toJson(scope.obj.nestedObjs);
/// remove quotes from property names
nestedObjs = nestedObjs.replace(/\"([^(\")"]+)\":/g, "$1:");
var nestedDir = "<div ng-init='nestedObjs=" + nestedObjs + "'><div-repeater ng-repeat='nestedObj in nestedObjs track by $index' obj='nestedObj'></div-repeater></div>";
el.append(nestedDir);
}
$compile(el)(scope);
element.append(el);
}
};
});
Controller
app.controller('MainCtrl', function($scope) {
$scope.objs = [{
id: '1',
nestedObjs: [{
id: 'a'
}, {
id: 'b',
nestedObjs: [{
id: 'i'
}, {
id: 'ii'
}]
}]
}, {
id: '2'
}, {
id: '3',
nestedObjs: [{
id: 'a',
nestedObjs: [{
id: 'i'
}]
}, {
id: 'b',
nestedObjs: [{
id: 'i'
}, {
id: 'ii'
}]
}]
}];
});
Markup
<div-repeater ng-repeat="obj in objs track by $index" obj="obj"></div-repeater>

Angular tree directive with only one directive - too much recursion?

I am trying to avoid writing the compile and/or link functions. I only want to use the controller function. Why am I getting "too much recursion"?
The data:
$scope.myTree = {
name: "Root",
id: 1,
items: [
{
name: "Arts",
id: 12,
items: [
{ name: "Sculpture", id: 220 },
{ name: "Painting", id: 221 },
{ name: "Music", id: 222 }
]
},
{
name: "Science",
id: 45,
items: [
{ name: "Biology", id: 345 },
{ name: "Chemistry", id: 346 },
{ name: "Physics", id: 347}
]
}
]
};
The markup:
<tree data="myTree" labelfield="name" valuefield="id" childrenfield="items">
<div>
This is the custom node content.
</div>
The directive:
angular.module("app").directive("tree", function ($compile) {
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
labelfield: "#",
valuefield: "#",
childrenfield: "#",
data: "="
},
controller: function ($scope) {
$scope.children = []; // remember - these are NOT the model's children!!!
if ($scope.data[$scope.childrenfield] !== undefined) {
for (var i = 0; i < $scope.data[$scope.childrenfield].length; i++) {
$scope.children.push({
label: $scope.data[$scope.childrenfield][i][$scope.labelfield],
value: $scope.data[$scope.childrenfield][i][$scope.valuefield]
});
}
}
},
template: "<ul><li ng-transclude></li>" +
"<li ng-repeat='child in children'> {{child.label}}" +
"<tree labelfield='labelfield' valuefield='valuefield' childrenfield='childrenfield'></tree>" +
"</li>" +
"</ul>"
};
});
If I remove the tag in the template, it will show only the first level, otherwise, I'll get infinite recursion.
What is missing? What shouldn't be there?
It appears that while you can't include the same directive inside itself, you can include another directive that includes the first one.
angular ui tree appears to do this with the TreeNode and TreeNodes directives.
[This is a late answer, but I think some people will find this useful.]
Nested directives will cause that "too much recursion" error. You may use RecursionHelper from this post.
After adding RecursionHelper service to your angular module, you just need to compile your directive element using RecursionHelper.compile function.
compile: function(element) {
// Use the compile function from the RecursionHelper,
// And return the linking function(s) which it returns
return RecursionHelper.compile(element);
}

how to make custom directive or pass variable in directive in angular js

I am trying to make bar chart in angular .I am able to make in jquery (using highchart.js file).This is link which I am able to make in jquery code
http://plnkr.co/edit/SD1iSTBk8o1xi3unxKeE?p=preview .I am getting the correct output.But the same thing I need to show using angularjs using directive .So I try to make directive .My issue is how I will pass my parameter in directive
here is my angular code.
http://plnkr.co/edit/LwonlkbCy3asHXyToVMz?p=catalogue
// Code goes here
angular.module('contactsApp', ['ionic'])
.controller('MainCtrl', function($scope) {
}).directive('chartTest',function(){
return {
restrict: 'E',
scope:{
},
link :function(scope, element, attrs){
element.highcharts({
chart: {
type: 'bar'
},
title: {
text: chart_title
},
xAxis: {
categories: xAxisarry
},
yAxis: {
title: {
text: 'Fruit eaten'
}
},
series: [{
name: names[0],
data: janeData
}, {
name: names[1],
data: joneData
}]
});
}
}
});
I want it look like same as in jquery ? can we pass variable in directive using scope?
You need to pass the parameters from the isolated scope of your directive, and then inside your directive you need to use $ on directive element to make highcharts method available.
Markup
<chart-test chart-title="chartTitle" x-axisarry="xAxisarry"
y-axis="yAxis" json-data="janeData" names="names"></chart-test>
Controller
.controller('MainCtrl', function($scope) {
$scope.chartTitle = "";
$scope.xAxisarry = ['Apples', 'Bananas', 'Oranges'];
$scope.yAxis = 'Fruit eaten';
$scope.data = {
jane: [1, 0, 4],
jone: [5, 7, 3]
};
$scope.names = ["jane", "jone"];
})
Directive
.directive('chartTest', function() {
return {
restrict: 'E',
scope: {
chartTitle: '=',
xAxisarry: '=',
yAxis: '=',
jsonData: '=',
names: '='
},
link: function(scope, element, attrs) {
$(element).highcharts({
chart: {
type: 'bar'
},
title: {
text: scope.chartTitle
},
xAxis: {
categories: scope.xAxisarry
},
yAxis: {
title: {text: 'Fruit eaten'}
},
series: [{
name: scope.names[0],
data: scope.jsonData['jane']
}, {
name: scope.names[1],
data: scope.jsonData['jone']
}]
});
}
}
});
Working Plunkr

Angular UI Bootstrap's Radio Button inside a directive with ng-repeat does not show default value properly

Essentially, I have a directive that is used as a 3 way filter using a radio button. Unfortunately, it needs to have a default state and that default state has to actually be shown in the UI, the problem is that although the model is updated properly, the UI is not. Here is a plunkr that demonstrates the issue:
http://plnkr.co/edit/8pljDFyRfInI4Q0qSTmL?p=preview
The directive is used the following way:
<filter model="model"/>
Where the model is defined as this.model = { value: {} } in the controller
Here is an updated code.
At first I want to say what ng-model directive works only with input elements,
and also I changed the isActive: 'Yes/No' to true/false
// Code goes here
var filters = angular.module('filters', ['ui.bootstrap']);
filters.controller('FilterCtrl', function() {
this.model = { value: {} };
});
filters.directive('filter', function () {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<div class="btn-group">' +
'<label ng-repeat="choice in choices" class="btn btn-{{ choice.buttonClass }}" ng-class="{active: choice.value.isActive}"' +
'btn-radio="{{ choice.value }}"><i class="fa {{ choice.icon }}"></i> {{ choice.name }}</label>' +
'</div>',
link: function (scope, element, attrs) {
scope.choices = [
{ value: { isActive: true }, name: 'Active', buttonClass: 'default', icon: 'fa-circle' },
{ value: null, name: 'Both', buttonClass: 'primary', icon: 'fa-arrows-h' },
{ value: { isActive: false}, name: 'Inactive', buttonClass: 'danger', icon: 'fa-circle-o' },
];
scope.model.value = _.first(_.filter(scope.choices, { value: { isActive: 'Yes' } })).value;
}
}
});

Resources