Angular wrapper component takes additional directives - angularjs

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.

Related

Directive to show error messages

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.

How to Compile HTML String inside foreach loop

I am trying to compile HTML String directive which is place inside foreach loop. I even tried putting scope but still not working. My sample code looks like this:
hotelierApp.directive('maps', ['$translate', '$filter', '$compile',
function ($translate, $filter, $compile) {
return {
restrict: "A",
replace: true,
scope: true,
link: function (scope, element, attrs) {
_.each(scope.properties, function (property) {
var strHtml = $compile('<div><div class=\'hotelMarker\'>' +
'<div class=\'hotelIcon\'>' +
'<img alt=\'\' class=\'menuShadow\' src=\'{{PropertyThumbnail}}\' height=\'45\' width=\'45\'/>' +
'</div>' +
'<div class=\'hotelText\'>' +
'<span class=\'hotelHeader\'>{{PropertyName}} ' +
'</span>' +
'</div>' +
'</div></div>')(property);
return function () {
if (infoWindow) {
infoWindow.close();
}
infoWindow.setContent(strHtml[0].innerHTML);
infoWindow.open(map, markerOtherHotel, strHtml[0].innerHTML);
}
})(markerOtherHotel, property.PropertyID));
});
},
template: ''
}}]);
Please help
property is not a scope object and cannot be used with $compile in this way. Instead, you could do something like this:
var strHtml = $compile('<div><div class=\'hotelMarker\'>' +
'<div class=\'hotelIcon\'>' +
'<img alt=\'\' class=\'menuShadow\' src=\'' + property.PropertyThumbnail + '\'' +
' height=\'45\' width=\'45\'/>' +
'</div>' +
'<div class=\'hotelText\'>' +
'<span class=\'hotelHeader\'>' + property.PropertyName + ' ' +
'</span>' +
'</div>' +
'</div></div>')(scope);
See this Plunker for a working example.

radio button directive not binding the ngModel

i am trying to create a generic radioButton directive which will take options from controller for display:
<cs-radio-field options="gender" ng-model="genderValue"></cs-radio-field>
and the options would be like
$scope.gender = { label: "Gender", required:true, valueList: [{ text: "Male", value: "yes" },{text:"Female", value:"no"}] };
the directive is defined as:
app.directive("csRadioField", function () {
var templateHtml = function () {
return '<div ng-form="myform">' +
'<div class="control-group" class="{{options.class}}">' +
'<div class="control-label">{{options.label || "Radio"}} {{ options.required ? "*" : ""}} </div>' +
'<div class="controls">' +
'<div class="radio" ng-repeat="(key, option) in options.valueList">' +
'<label> <input type="radio" name="myfield" ng-value="option.value" ng-model="ngModel" ng-required="options.required" />{{option.text}} </label>' +
'</div>' +
'<div class="field-validation-error" data-ng-show="myform.myfield.$invalid && myform.myfield.$dirty"> ' +
'<div data-ng-show="myform.myfield.$error.required">{{options.label}} is required!!!</div>' +
'</div>' +
'</div>' +
'</div>' +
'</div>';
};
return {
scope: { options: '=', ngModel: '=' },
required: ['ngModel', '^form'],
restrict: 'E',
template: templateHtml,
};
});
but the ngModel is not binding in the parent scope.. mostly because of new scopes being created by ng-repeat.
how to solve this issue?
the plunker link: http://plnkr.co/edit/myS5hXwxjoDEqQI2q5Q7?p=preview
Try this in your template:
ng-model="$parent.ngModel"
DEMO
You're correct that ng-repeat creates child scopes. You're actually binding to child scopes' property.

Angularjs : Loop all isolated scope Directive Templates, on click of one

I have html:
<div ng-repeat="mydata in data" class="ng-scope ng-binding">
<p class="ng-binding">{{mydata.postdata}}</p>
<div my-rating rating-value="rating" data-cat="post" data-id="mydata.id" ></div>
<div ng-repeat="childData in mydata.personRelatedData">
{{childData.personName}}
<div my-rating rating-value="rating" data-cat="person" data-id="childData .id" >
</div>
</div>
I have a Directive :
myDirectives.directive('myRating', function () {
return {
restrict: 'A',
template: '<div><ul>' +
'<li ng-repeat="i in getNumber(myNumber)" ng-click="toggle($index)" id=$index>' +
'<div ng-switch="switchPoint<$index">'+
'<div ng-switch-when=true><img ng-src="img/{{Unrated}}"></div>'+
'<div ng-switch-when=false><img ng-src="img/{{Rated}}"></div>'+
' </div>' +
'</li></ul></div>',
scope: {
ratingValue: '=',
dataCat: '=',
dataId: '=',
readonly: '#',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
scope.toggle= function(val) {
scope.ratingValue = val + 1;
scope.onRatingSelected({rating: null});
scope.switchPoint = val;
}
}
}
Currently i have implemented that on page load, the rating from the db comes, and while rendering the template, if rating for that ID exists, then it is rated on the isolated rating template too.
Now what I want is that on click of a rating, the execution should rate that ID celebrity, but also loop the other isolated templates, to see if ratings for that ID exist.. and if so, we can update their rating too.
How can I loop the other isolated templates on click of one?
Don't store the rating value in the scope of the directive, store the rating in a location that is accessible to all of the items.
mydata is a perfect location for this. Instead of passing the mydata.rating property directly to the directive (and thus passing a value), pass the whole mydata object, and reference the mydata.rating property within the directive. Since you're passing an object to the directive, it will be a reference, not a value, i.e. all of the directives which are passed the same mydata object will be looking at the same object, not copies of a particular property. You could achieve the same goal by making the rating property an object as well instead of a value.
EDIT: in response to comments, you could change it to be more direct.
myDirectives.directive('myRating', function () {
return {
restrict: 'A',
template: '<div><ul>' +
'<li ng-click="setRating(1)">' +
' <img ng-src="img/{{ myData.rating >= 1 ? Unrated : Rated}}">' +
'</li>' +
'<li ng-click="setRating(2)">' +
' <img ng-src="img/{{ myData.rating >= 2 ? Unrated : Rated}}">' +
'</li>' +
'<li ng-click="setRating(3)">' +
' <img ng-src="img/{{ myData.rating >= 3 ? Unrated : Rated}}">' +
'</li>' +
'<li ng-click="setRating(4)">' +
' <img ng-src="img/{{ myData.rating >= 4 ? Unrated : Rated}}">' +
'</li>' +
'<li ng-click="setRating(5)">' +
' <img ng-src="img/{{ myData.rating >= 5 ? Unrated : Rated}}">' +
'</li>' +
'</ul></div>',
scope: {
myData: '=',
readonly: '#',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
scope.setRating = function(val) {
scope.myData.rating = val;
scope.onRatingSelected(scope.myData);
}
}
}
I don't expect this example to work with just copy/paste to your problem; it's just meant as an idea to get your rolling.

Multiple directives [directive#1, directive#2] asking for isolated scope on

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.

Resources