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.
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.
My ngMessages doesnt work inside my directives template!
I have a directive myInput with a template and a link function, inside the template function I create template string for a wrapped <label> and <input>.
Inside the Link function I use the require: '^form' FormController and retrieve the form name. Then I'm putting a ngMessages block after the wrapped elements.
(function () {
'use strict';
angular
.module('app.components')
.directive('myInput', MyInput);
/*#ngInject*/
function MyInput($compile, ValidatorService, _, LIST_OF_VALIDATORS) {
return {
require: '^form',
restrict: 'E',
controller: MyInputController,
controllerAs: 'vm',
bindToController: true,
template: TemplateFunction,
scope: {
label: '#',
id: '#',
value: '=',
validateCustom: '&'
},
link: MyInputLink
};
function MyInputController($attrs) {
var vm = this;
vm.value = '';
vm.validateClass = '';
vm.successMessage = '';
vm.errorMessage = '';
}
function TemplateFunction(tElement, tAttrs) {
return '<div class="input-field">' +
' <label id="input_{{vm.id}}_label" for="input_{{vm.id}}" >{{vm.label}}</label>' +
' <input id="input_{{vm.id}}" name="{{vm.id}}" ng-class="vm.validateClass" type="text" ng-model="vm.value" >' +
'</div>';
}
function MyInputLink(scope, element, attrs, form){
var extra = ' <div ng-messages="' + form.$name + '.' + scope.vm.id + '.$error">' +
' <div ng-messages-include="/modules/components/validationMessages.html"></div>' +
' </div>';
$(element).after(extra);
}
}
})();
Usage:
<h1>Test</h1>
<form name="myForm">
<my-input label="My Input" id="input1" value="vm.input1"></my-input>
-------
<!-- this block is hardcoded and is working, it does not come from the directive! -->
<div ng-messages="myForm.input1.$error">
<div ng-messages-include="/modules/components/validationMessages.html"></div>
</div>
</form>
Instead of adding the ngMessages block inside the link function, add it inside the compile function.
It is not as handy as in the link funciton because of the missing FormController, but it works :-)
Here the code:
compile: function(tElement, tAttrs){
var name = tElement.closest('form').attr('name'),
fullFieldName = name + '.' + tAttrs.id; // or tAttrs.name
var extra = '<div ng-messages="' + fullFieldName + '.$error">' +
' <div ng-messages-include="/modules/components/validationMessages.html"></div>' +
'</div>';
$(element).after(extra);
Here is what I did, I added to scope, myForm: '=' then in the directive's template referred to <div ng-messages="vm.myForm[vm.id].$error" >
I feel this is much cleaner than mucking around in the link function.
I have the code below. predictions['Team1 expected goals'] comes up as undefined in the donutChart.js directive even though the value is defined on the second line. How do I properly pass that to the directive?
<div>
<div>{{ (fixture.team1.name.short_name || fixture.team1.name.gsm_name) }}</div>
<div>{{ predictions['Team1 expected goals'] | number : 2 }}</div>
</div>
<div>
<donut-chart team1="predictions['Team1 expected goals']"
team2="predictions['Team2 expected goals']">
</donut-chart>
</div>
The below works as expected:
<donut-chart team1="1.26" team2="0.81"></donut-chart>
Here is the code for the donutChart.js directive. Note also that the structure is, an overall controller calls a directive (called models-prices) which calls this chart directive.
angular.module('stratabet')
.directive('donutChart', function(appVersion) {
'use strict';
var directive = {
priority: 0,
restrict: 'E',
scope: {
team1: '=',
team2: '='
},
controller: function($scope) {
console.log('donutChart controller - team1: ' + $scope.team1 + ', team2: ' + $scope.team2);
},
link: function(scope, element, attrs) {
console.log('donutChart - team1: ' + scope.tea1 + ', team2: ' + scope.team2);
}
}
In the line,
console.log('donutChart - team1: ' + scope.tea1 + ', team2: ' + scope.team2);
there is a typo in scope.tea1, it should be $scope.team1 as there is no property tea1 on scope
it should be
console.log('donutChart - team1: ' + scope.team1 + ', team2: ' + scope.team2);
I have a directive for showing a 'share to social networks menu'. It works fine in normal page but when i use it on a lightbox (that fetchs it's data after clicking on an item), it fails to pass attributes to share menu directive without any error. The variables are empty.
<item-actions data-itemid="{{popup.id}}"></item-actions>
Directive:
var itemActions_tmpl =
'<div>' +
// share button
'<div class="share-w">' +
'<button type="button" class="button radius share" value="share" >' +
'</button>' +
'<ul>' +
'<li><a target="_blank" data-ng-href="{{::fblink}}">Facebook</a></li>' +
'<li><a target="_blank" data-ng-href="{{::gplink}}">Google +</a></li>' +
'<li><a target="_blank" data-ng-href="{{::twlink}}">Twitter</a></li>' +
'</ul>' +
'</div>' +
'</div>';
jApp.directive('itemActions', ['APP_CONFIG', function(APP_CONFIG){
return {
replace:true,
scope: {},
restrict: 'AE',
template: itemActions_tmpl,
link: function(scope, Elem, Attrs, controller) {
scope.fblink = 'https://www.facebook.com/sharer/sharer.php?u=' + APP_CONFIG.BASE_ITEM_URL + Attrs.itemid;
scope.gplink = 'https://plus.google.com/share?url=' + APP_CONFIG.BASE_ITEM_URL + Attrs.itemid;
scope.twlink = 'http://twitter.com/home?status=' + APP_CONFIG.BASE_ITEM_URL + Attrs.itemid;
}
};
}]);
How can i fix this?
I used $q and problem solved. Just waiting for data and then opening popup.
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.