I am trying to add $watch in my directive. The directive is for wizard creation , when I read an existing array and use ng-repeat in directive it works as expected but If I update my array the newly added items in array doesn't add in directive to create wizard.
Please refer This plunker to understand the issue. At this plunker script.js has array data which I will be updating on a button click and read them back in ng-repeat under wizard directive on html page.
the directive is
directive('wizard', function($timeout, $compile) {
var opts = {
headerTag: "h3",
bodyTag: "section",
transitionEffect: "slideLeft",
autoFocus: true,
enableAllSteps: true,
enableKeyNavigation: false,
enableCancelButton: true,
enableFinishButton: true,
showFinishButtonAlways: false
};
return {
restrict: 'E',
replace: true,
template: '<div class="mywizard"></div>',
compile: function (tEl) {
return function(scope, element, attrs, ngModel) {
var stepsEl;
element.wrapInner('<div class="steps-wrapper">');
element.children('.steps-wrapper').append(tEl.context.innerHTML);
var content = $compile(element.contents())(scope);
$timeout(function () {
element.children('.steps-wrapper').steps(opts);
})
}
}
};
});
Please share idea where should I apply the watch.
Related
I have following code
// Template
<span>
hello world!
</span>
// Directive
'use strict';
var referenceFieldTemplate = require('./reference-field.html');
module.exports = directiveFunction;
/* #ngInject */
function directiveFunction($http) {
return {
restrict: 'E',
scope: {
fieldName: '=',
fieldValue: '='
},
templateUrl: referenceFieldTemplate,
controller: function($scope){
console.log($scope);
},
link: function(scope, element, attr) {
element.bind('mouseover',function(e) {
console.log(e);
});
}
}
}
I'm trying to render custom directive in uib-tooltip using following code
function eventRender(event, element, view) {
var hoverMarkup = '\'<reference-field field-name="test" field-value="test"></reference-field>\''
element.attr({
'uib-tooltip-html': hoverMarkup,
'tooltip-append-to-body': true,
'tooltip-class': 'tooltip-wrapper'
});
$compile(element)($scope);
};
After compilation, the directive is not there in DOM. I want to understand the reason why it's not getting rendered. There are no errors in console.
Thanks in advance.
I've done recently something similar using "uib-tooltip-template", put all your html code inside the template and it will work just fine.
You just need to create a html file with your directive inside and asign it via $scope to the tooltip.
Assume this directive:
<validation-errors field="someField">Some errors: {{errors}}</validation-errors>
I thought I could create the directive function simple as this:
return {
require: '^form',
restrict: 'E',
link: link,
scope: {
field: '#'
},
transclude: true,
template: '<span ng-show="errors" class="alert-danger" ng-transclude></span>'
};
function link(scope, el, attr, ctrl, transcludeFn) {
scope.errors = ctrl.Validator.getErrors(attr.field);
}
But since Transclusion is the process of extracting a collection of DOM element from one part of the DOM and copying them to another part of the DOM, while maintaining their connection to the original AngularJS scope from where they were taken. (from docs), the scope doesn't work like I thought it would.
So I tried this which works, except that the "Some errors" part is duplicated:
transcludeFn(function(clone, transScope) {
scope.errors = transScope.errors = ctrl.Validator.getErrors(attr.field);
el.append(clone);
});
It doesn't work if I remove el.append(clone);.
What's the best way to make the transcluded content share the directive template's scope?
If you want to create the errors using the directive, give something like this a try, I've updated the code so that it compiles the template as well, now working exactly as the ng-transclude directive would out of the box.
'use strict';
/* Directives */
angular.module('myApp.directives', []).
directive('errordirective', ['$compile',function($compile) {
return {
restrict: 'E',
scope: {
field: '#',
},
transclude: true,
//Create some error text
controller: function() {
this.errorText = "Some Errors We Created";
//Make the template available to the link fn, and make it an angular element.
this.template = angular.element('<span class="alert-danger" ng-transclude></span>')
},
//Make the controller available easily
controllerAs: 'Errors',
//Bind the scope properties to the controller, only works with controllerAs
bindToController: true,
template: '<span class="alert-danger" ng-transclude></span>',
link: function(scope, elem, attrs, ctrl, transcludefn) {
//Replace the original element with the new one that has the error text from our controller
transcludefn(scope, function(el) {
var template = ctrl.template;
var html = el.html();
//Add the transcluded content to the template
template.html(html);
//Compile the template with the appropriate scope
template = $compile(template)(scope);
//Replace with the new template
elem.replaceWith(template);
});
}
};
}]);
In this example, I have two AngularJS KendoDatePickers. The first one works perfectly, if you click on the button you open the calendar. The second one is within a directive that has the transclude attribute set to true. If you click on the second button, you get an error.
My understanding is that the scope of the transcluded portion inherits from the control scope, so this should work. Where am I wrong?
This is the plunk
HTML
<input kendo-date-picker="picker" />
<button ng-click="openPicker()">Open Date Picker</button>
<my-window>
<input kendo-date-picker="picker2" />
<button ng-click="openPicker2()">Open Date Picker 2</button>
</my-window>
Javascript
var app = angular.module("app", [ "kendo.directives" ]);
app.controller('MyCtrl', function($scope) {
$scope.openPicker = function () {
$scope.picker.open();
};
$scope.openPicker2 = function () {
$scope.picker2.open();
};
});
app.directive('myWindow', function() {
return {
transclude: true,
scope: {
someVar: '='
},
restrict: 'EA',
template: function(element, attrs) {
var html = '<div ng-transclude></div>';
return html;
}
};
});
There are two things about your code:
first: you create an isolatedScope so you do not have access to the controller scope inside the directive scope.
second: transcluded content get their own scope. One way to work around this is by not using transclude at all, like the example below:
return {
transclude: false,
restrict: 'EA',
template: function(element, attrs) {
var html = '<div>'+element.html()+'</div>';
return html;
}
or use the link function and manually transclude the element with the scope of the directive
I am trying to load a 'class' directive using ng-class. but my directive is never loaded when i do that. The directive is a multipurpose directive, and I don't want to create an isolated scope on this. it will be loaded only when required, based on ng-class conditions hence not using attribute or element directive. has anyone tried doing this and succeeded?
this directive is called using <div ng-class="someClass {{tooltip: enabled}}"></div>
here enabled is a scope variable.
app.directive('tooltip', ['$timeout', '$location', '$rootScope', function (timer, $location, $rootScope) {
return {
restrict: 'C',
transclude: true,
link: function (scope, element) {
var printContent = function () {
/* uses the content of .tooltip-content if it is a complex html tooltip,
otherwise
you can use the title attribute for plaintext tooltips
*/
var tooltipContent = $(element).find('.tooltip-content').html();
if (!tooltipContent) {
tooltipContent = $(element).attr('title');
}
$(element).tooltip({
content: tooltipContent,
items: "img, a, span, button, div",
tooltipClass: "tooltip",
position: { my: "left+30 top", at: "right top", collision: "flipfit" },
show: { effect: "fadeIn", duration: "fast" },
hide: { effect: "fadeOut", duration: "fast" },
open: function (event, ui) { $rootScope.tooltipElement = event.target; }
});
};
timer(printContent, 0);
}
};
}]);
Interesting issue. It seems that you don't want to use the ng-class directive since that doesn't recompile the content after adding the class. You'll likely want to create your own dynamic-class directive that actually recompiles when the value is true:
app.directive('dynamicClass', function($compile) {
return {
scope: {
dynamicClassWhen: '=',
dynamicClass: '='
},
link: function(scope, elt, attrs) {
scope.$watch('dynamicClassWhen', function(val) {
if (val) {
console.log(val);
elt.addClass(scope.dynamicClass);
$compile(elt)(scope);
}
});
}
};
});
You may need to modify this for the ability to remove the class and depending on if the $compile is sufficient for you or if you need to further manipulate the html, but this seems to be the right track for you. I made a fiddle with this in action.
Hope this helps!
I'm using the $dialog directive to show a dialog in my application. The dialog is opened from another directive :
angular.module('axa.directDebit.directives').directive("mandateHistoryDetail", ['$dialog', function($dialog) {
return {
restrict: 'E',
template: '<a class="btn btn-small" ng-click="openDialog()">Détail</a>',
scope: {
model: '='
},
link: function (scope, element, attrs) {
scope.openDialog = function(){
var d = $dialog.dialog({
backdrop: true,
keyboard: true,
backdropClick: true,
dialogFade: true,
templateUrl: 'app/directDebit/views/mandates.detail.history.detail.html',
controller: 'mandates.detail.history.detail.ctrl',
resolve: {
data: function () {
return scope.model;
}
}
});
d.open();
}
},
controller: 'mandates.detail.history.detail.ctrl'
}
}]);
The problem I'm having, is that from the dialog's controller, I would like to access the calling directive's scope. In particular the 'model' property in the above code.
I've tried using resolve, but the from the dialog controller I don't know how to get hold of data.
Any idea what I should change ?
In the dialog controller, you should just add it as a dependency.
You called it data so it should be -
angular.module('yourModule').controller('mandates.detail.history.detail.ctrl',
function($scope, data){
...
});
Just as a side note - I would extract the behavior of opening the $dialog to an outside view controller and not inside a directive, 'cause it looks like application logic to me and directives should aspire to be reusable.