How to add a new directive to an element from a directive? - javascript

I have a directive that I want to add other directives in its place. I am able to add the new directive's attribute but it does not seem to compile and do anything.
hat I have so far is: plunkr
app.directive('xxx', function($compile) {
return {
priority: '3',
compile: function(e, a) {
e.removeAttr('xxx');
a.$set('yyy','');
return function(scope, el, attrs) {
el.html('step 1');
$compile(el)(scope);
};
}
};
});
app.directive('yyy', function() {
return {
priority: '1',
compile: function(e, a) {
return function(scope, el, attrs) {
el.html('step 2');
};
}
};
});
app.directive('zzz', function() {
return {
priority: '2',
compile: function(e, a) {
return function(scope, el, attrs) {
console.log('zzz');
};
}
};
});
the html is
<div xxx zzz></div>
the current output is:
<div zzz yyy>Step 2</div>
which is what I want except that directive zzz is run twice!! and that is not good.
if I do not $compile in directive xxx after I add directive yyy, yyy does not execute and the result is
<div zzz>Step 1</div>
if I do not first remove xxx before $compile then the app crashes due to what I assume is infinitely trying to compile the xxx directive.
The priorities of the directives don't seem to have any effect.
What I need is to be able to add one or more directives from inside a directive without recompiling other directives on that element.

Related

Angularjs directive duplicates the elements inside it

(function () {
'use strict';
angular.module('product')
.directive('sampledirective', ['$document') {
return {
restrict: 'E',
replace: true,
scope: {
data: '=',
btnClick: '&'
},
link: function (scope, element, attr) {
var compiled = $compile(template)(scope);
angular.element(element).replaceWith(compiled);
element = compiled;
};
};
}]);
})();
I have a directive which replaces the elements inside it.
I have a weird issue which replaces the elements mulitple time in the directive .
Duplicates the elements in the below bolded line which should not happen.
angular.element(element).replaceWith(compiled);
Please let me know why the elemenst are duplicated and let me know how to avoid it .
sample
Actual
cool cool
expected
cool
The following directive only replaces the content once in my case. If this dosn't solve your problem maybe you could provide a small working example or so. Also note that if you use an isolated scope for your directive you should provide a template, as stated in this post.
angular.module('product').directive("sampledirective", function ($compile) {
return {
template: '',
restrict: 'E',
scope: {
data: "=data",
btnClick: '&'
},
link: function (scope, element, attrs) {
var template = "<div>foo</div>"
var compiled = $compile(template)(scope);
element.replaceWith(compiled);
}
}
});

angular-bootstrap add new directive dosn't work

I want to create a new directive into ui.boostrap.accordion module to avoid accordion open click event.
I have the following code in another file.js:
angular.module('ui.bootstrap.accordion')
.directive('accordionGroupLazyOpen', function() {
return {
require: '^accordion',
restrict: 'EA',
transclude: true,
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'template/accordion/accordion-group.html';
},
scope: {
heading: '#',
isOpen: '=?',
isDisabled: '=?'
},
controller: function() {
this.setHeading = function(element) {
this.heading = element;
};
},
link: function(scope, element, attrs, accordionCtrl) {
accordionCtrl.addGroup(scope);
scope.openClass = attrs.openClass || 'panel-open';
scope.panelClass = attrs.panelClass;
scope.$watch('isOpen', function(value) {
element.toggleClass(scope.openClass, value);
if (value) {
accordionCtrl.closeOthers(scope);
}
});
scope.toggleOpen = function($event) {
};
}
};
})
The problem is when I execute the app I get the following error:
Controller 'accordionGroup', required by directive
'accordionTransclude', can't be found!
Error link
Any ideas?
As I see from the source code ( maybe not your version but still the same):
// Use in the accordion-group template to indicate where you want the heading to be transcluded
// You must provide the property on the accordion-group controller that will hold the transcluded element
.directive('uibAccordionTransclude', function() {
return {
require: '^uibAccordionGroup', // <- look at this line in your version
link: function(scope, element, attrs, controller) {
scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
if (heading) {
element.find('span').html('');
element.find('span').append(heading);
}
});
}
};
So I guess it tries to find a parent directive in the view that matches accordionGroup but since you add the accordionGroupLazyOpen and not the accordionGroup it cannot find it.
In the error page you provided states:
This error occurs when HTML compiler tries to process a directive that
specifies the require option in a directive definition, but the
required directive controller is not present on the current DOM
element (or its ancestor element, if ^ was specified).
If you look in the accordion-group-template file you will see that the accordionTransclude directive gets called there.

Angularjs binding array element ng-repeat in directive

I'm trying to display the elements of an array using ng-repeat and a directive. The directive part is important to the solution. However the element of the array is not getting bound and displays an empty value.
The fiddle can be found at http://jsfiddle.net/qrdk9sp5/
HTML
<div ng-app="app" ng-controller="testCtrl">
{{chat.words}}
<test ng-repeat="word in chat.words"></test>
</div>
JS
var app = angular.module('app', []);
app.controller("testCtrl", function($scope) {
$scope.chat = {
words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]
};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {}
}
});
OUTPUT
["Anencephalous","Borborygm","Collywobbles"]
•
•
•
Expected output
["Anencephalous","Borborygm","Collywobbles"]
•Anencephalous
•Borborygm
•Collywobbles
Appreciate your help
You didn't bind word.
You have used isolate scope. If you don't bind with it's scope property,it won't work.
scope: {
word: '='
},
Try like this
<test word="word" ng-repeat="word in chat.words"></test>
DEMO
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.chat= {words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
priority: 1001,
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
Your directive needs to run before ng-repeat by using a higher priority, so when ng-repeat clones the element it is able to pick your modifications.
The section "Reasons behind the compile/link separation" from the Directives user guide have an explanation on how ng-repeat works.
The current ng-repeat priority is 1000, so anything higher than this should do it.

Sharing Data between two Directives in AngularJS

I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >

Can not find element in postlink of directive creation in angularjs

I am trying to create a directive for JustGauge in AngularJS. My html view is something like this:
<div id="guageContainer" class="row" ng-controller="guageCtrl">
<gauge ng-repeat="ind in indicators" model="ind"></gauge>
</div>
and my directive is:
angular.module("pgHome", [])
.directive("gauge", function ($compile) {
return {
restrict: "E",
scope: { model: "=model" },
compile: function (tElement, tAttrs, transclude) {
tElement.append('<div id="{{model.Name}}"></div>');
return {
post: function (scope, iElement, iAttrs, controller) {
console.log(scope.model.Name);
console.log($("#" + scope.model.Name).length);
var g = new JustGage({
id: scope.model.Name,
value: 348,
min: 120,
max: 400,
title: "test",
label: ""
});
}
};
}
};
});
When I load the page containing this directive, JustGauge raises an error saying that it can't find an element with corresponding id. Just to mention that the first console.log works well but the second console.log returns 0. Also I am new to angularjs and I couldn't solve this problem!
Wrap your JustGage() call in a $timeout:
.directive("gauge", function ($compile, $timeout) {
...
$timeout(function() {
var g = new JustGage({...});
});
This gives the browser a chance to render the additional HTML you added in the compile function. Then the plugin can find the newly added div.

Categories

Resources