I'm attempting to build a new directive on top of an already existing directive but I am halted in my proces. When loading the page I'm facing the following error:
Multiple directives [directive#1, directive#2] asking for isolated scope on <easymodal title="Test-Title" text="Text-Text" oncancel="show = false" onok="alert();">
The base directive looks like this:
Rohan.directive('easymodal', function () {
return {
restrict: 'E',
// priority: 200,
transclude: true,
replace: true,
scope:{
showModal: "=show",
callback: "=closeFunction",
dismissable: '&'
},
template:
'<div data-ng-show="showModal" class="modal-container">' +
'<div class="modal-body">' +
'<div class="title"><span data-translate></span><a data-ng-show="dismissable" data-ng-click="dismiss()">x</a></div>' +
'<div data-ng-transclude></div>' +
'</div>' +
'<div class="modal-backdrop" data-ng-click="dismiss()"></div>' +
'</div>'
};
});
And my wrapper directive looks like this:
Rohan.directive('easydialog', function () {
return {
restrict: 'E',
transclude: true,
scope: {title: '#',
text: '#',
onOk: '&',
onCancel: '&'},
replace: true,
template:
'<easymodal>' +
'{{text}} ' +
'<hr>' +
'<button ng-click="{{onCancel}}" value="Cancel"' +
'<button ng-click="{{onOk}}" value="Ok"' +
'</easymodal>'
};
});
My html looks like this:
<easydialog title="Test-Title" text="Text-Text" onCancel="show = false" onOk="alert();" />
At first I though my title attribute was conflicting so I removed that attribute in the html line and from my wrapper directive but it was not effective.
You need to change your easydialog template and wrap <easymodal> in a <div>.
Your problem simply is that you're adding a template argument inside the directive as well as adding a resolving template tag named <easydialog> in your actual HTML template. You have the choose either the one or the other.
Related
I have a directive that looks something like the following:
.directive('textInput', function() {
return {
restrict: 'E',
replace: true,
transclude: false,
compile: function(element, attrs) {
var html =
'<div class="form-group">' +
'<label>{{ \'' + attrs.label + '\' | translate }}</label>' +
'<input type="text" class="form-control input-sm" placeholder="' + attrs.placeholder +
'" ng-model="' + attrs.ngModel + '"' + attrs.directives + '>' +
'</div>';
var newElem = $(html);
element.replaceWith(newElem);
return function (scope, element, attrs, controller) { };
}
};
})
Note that the directives attribute was a string that added additional attribute information (e.g. tooltip info)
I have converted it to a 1.5 component but I'm not able to do the same with the directives definition.
.component('textInput', {
bindings: {
label: '#',
placeholder: '#',
directives: '<',
ngModel: '='
},
require: {
ngModelCtrl: 'ngModel'
},
template:
'<div class="form-group">' +
'<label ng-if="$ctrl.label">{{$ctrl.label | translate }}</label>' +
'<input' +
' type="text"' +
' class="form-control input-sm"' +
' placeholder="{{$ctrl.placeholder}}"' +
' ng-model="$ctrl.ngModel"' +
' {{$ctrl.directives}}>' +
'</div>'
})
<text-input directives="tooltip='foobar'"></text-input>
How can I pass in a string to the <text-input/> element such that the underlying template gets the correct information?
It is not possible, according to the docs,:
When not to use Components:
for directives that rely on DOM manipulation, adding event listeners
etc, because the compile and link functions are unavailable when you
need advanced directive definition options like priority, terminal,
multi-element when you want a directive that is triggered by an
attribute or CSS class, rather than an element
and in the same page it says that components doesn't have compile function.
I have validation set up, and am using ngMessages to show errors. However I want to create a wrapper directive around ngMessages that adds some HTML (to avoid a lot of code duplication).
So instead of having to write this on each input:
<div class="help-block validator">
<div ng-messages="registerForm.email.$error" ng-if="registerForm.email.$touched">
<p ng-message="required">This field is required.</p>
</div>
</div>
I'd just write something like this:
<error-message messages='{"required": "This field is required."}' error="registerForm.email.$error" touched="registerForm.email.$touched"></error-message>
The issue with my directive is that error and touched come up as strings, they don't get evaluated as JS expressions. I've tried to $eval them, but that throws an error.
Here's my directive:
angular
.module('myApp')
.directive("errorMessage", function () {
return {
restrict: "E",
replace: true,
scope: {
'messages': '=',
'error': '=',
'touched': '='
},
template: '<div class="help-block validator">' +
'<div ng-messages="error" ng-if="touched">' +
'<div ng-repeat="(key, message) in messages">' +
'<p ng-message="key">{{message}}</p>' +
'</div>' +
'</div>' +
'</div>',
link: function (scope, elem, attrs) {
scope.error = attrs.error;
scope.touched = attrs.touched;
scope.messages = scope.$eval(attrs.messages); // this works
}
};
});
Any idea how I should go about doing this?
Found the issue. Looks like attrs wasn't what I needed. The properties were already in the scope. The code I ended up with is:
angular
.module('myApp')
.directive("errorMessage", function () {
return {
restrict: "E",
replace: true,
scope: {
'messages': '=',
'error': '=',
'touched': '='
},
template: '<div class="help-block validator">' +
'<div ng-messages="error" ng-if="touched">' +
'<div ng-repeat="(key, message) in messages">' +
'<p ng-message="{{key}}">{{message}}</p>' +
'</div>' +
'</div>' +
'</div>',
link: function (scope, elem, attrs) {
}
};
});
I think ng-message-include meets your requirements. we can create new html file and place all of our messages in this file and just call it with ng-messages-include.
hope my answer is usable for you.
Here is my HTML part:
<div data-my-question="" data-question="Question text?">
<div data-yes="">
YES!
</div>
<div data-no="">
NO!
</div>
</div>
and here are my angular directives:
angular.module('myModule').directive('myQuestion', [
function () {
return {
restrict: 'A',
replace: true,
scope: {
question: '#'
},
transclude: true,
controller: function($scope){
$scope.answered = false;
$scope.yesActive = false;
$scope.activateYes = function (yesActive) {
$scope.answered = true;
$scope.yesActive = yesActive;
};
},
template: '<div>' +
'<div class="question-box" ng-class="{answered: answered}">' +
'<div class="question">' +
'{{question}}' +
'</div>' +
'<div class="buttons">' +
'Yes' +
'No' +
'</div>' +
'</div>' +
'<div ng-transclude></div>' +
'</div>'
};
}
])
.directive('yes', [
function () {
return {
restrict: 'A',
replace: true,
transclude: true,
template: '<p ng-show="answered && yesActive" ng-transclude></p>'
};
}
])
.directive('no', [
function () {
return {
restrict: 'A',
replace: true,
transclude: true,
template: '<p ng-show="answered && !yesActive" ng-transclude></p>'
};
}
]);
The problem is, that the child directives ('yes', 'no') can't access the 'answered' and the 'yesActive' variable from the parent directive, which has an isolated scope.
How can I achieve that the child directives react on value changes from the parent directive?
The transcluded elements won’t have access to the directive’s isolated scope.
This is how Angular works, an isolated scope (myQuestion) won’t inherit from any parent and the transcluded elements (Yes/No) will became siblings of the myQuestion’s scope.
Transcluded elements will be child of the original scope where they were defined, this way they are still able to bind instead of being isolated too.
If you check this JSBIN, in the console you will see that Yes/No scope have access to the $rootScope (where pizza is defined) while myQuestion’s isolated scope won’t.
You can get more info about this here.
Solution:
I like simple code so I'd move the Yes/No elements inside the question template if possible, but you could also communicate between directives.
Below is my issue
Issue with my custom control code.I have created a two custom control
<pv-Show-Box></pv-Show-Box>
<pv-Hello>Praveen</pv-Hello>
both are working fine but <pv-show-Box> is not working when it is in reverse order
like
<pv-Hello>Praveen</pv-Hello>
<pv-Show-Box></pv-Show-Box>
mumodule.directive('pvShowBox', function () {
return {
restrict: 'E',
template: '<div><input type="text" ng-model="txtfieldData" ></input> {{ txtfieldData }}</div>',
replace: true
}
});
mumodule.directive('pvHello', function () {
return {
restrict: 'E',
template: '<span ng-transclude>Hello </span>',
replace: true
};
});
Any idea??
There is a small problem in your code you are using ng-transclude but you have not mentioned transclude property in directive so just change below directive definition and it will work both ways
mumodule.directive('pvHello', function () {
return {
restrict: 'E',
transclude:true,
template: '<span ng-transclude>Hello </span>',
replace: true
};
});
I have two directives one which translates strings and one which creates a container with a title. (and some other functionality that was removed to make the example shorter)
Groupbox:
myapp.directive('groupbox', function () {
return {
restrict: 'E',
priority: 200,
template:
'<fieldset>' +
'<legend data-translate>{{title}}</legend>' +
'<div data-ng-transclude></div>' +
'</fieldset>',
transclude: true,
replace: true,
scope: {title: '#'}
};
});
Translate: (also simplified)
myapp.directive('translate', ['$filter', function ($filter) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var e = $(element);
var data = attrs.translate;
var results = $filter('I')(e.html(), data);
e.html(results.text);
if (results.tooltip) e.attr('data-tooltip', results.tooltip);
}
};
}]);
I use it like this:
<groupbox title='settings'>
content
</groupbox>
The idea is that the content of the "groupbox" gets put in the div and the title in the "legend". After this the legend needs to be translated by the translate directive. This translation does not happen (it just prints settings). When i replace '{{title}}' with 'settings' it does get translated.
How can i get the translate directive to operate on the results of the groupbox directive.
I fixed it by adding a compile function that puts the title in the legend directly (without binding). That way it is no different then any other use of my translate directive.
myapp.directive('groupbox', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
template:
'<fieldset>' +
'<legend>' +
'<span data-translate></span> - ' +
'<a data-ng-click="open = !open" data-translate>toggle</a>' +
'</legend>' +
'<div data-ng-show="open" data-ng-transclude></div>' +
'</fieldset>',
compile: function(element, attrs) {
element.children('legend').children('span').html(attrs.title);
}
};
});