Angularjs directive for checkbox with ng-model - angularjs

I have a directive that work on check-box that also has ng-model.
In the directive on link function the check box not get the value of the model.
It is work if add timeout( doesn't matter how long, even with 0 ).
My control and directive:
var myApp = angular.module("myApp",[])
.directive("checkBox", function($timeout){
return {
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
console.log("Check box is : " + element[0].checked);
scope.message += "Check box is : " + element[0].checked + " , ";
$timeout(function(){
scope.message += "Check box is : " + element[0].checked;
console.log("Check box is : " + element[0].checked);
},0);
}
}
});
function myCtrl($scope){
$scope.checkBoxModel = true;
$scope.message = "";
}
HTML:
<div ng-app="myApp" ng-controller="myCtrl" >
<input type="checkbox" ng-model="checkBoxModel" check-box>
<br/>
{{message}}
</div>
Fiddel - http://jsfiddle.net/myyjL/
Thanks in advance.

You probably should not do what you are trying to do, but it is possible.
Here is a plunkr that works, without timeout:
http://jsfiddle.net/myyjL/2/
.directive("checkBox", ['$timeout', function($timeout){
return {
restrict: 'A',
replace: false,
scope: {
ngModel:'='
},
transclude: true,
link: function (scope, element, attrs, ctrl) {
scope.$watch('ngModel', function(newValue){
scope.$parent.message = 'Checkbox value is: ' + newValue;
});
}
}
}])
The problem with that approach is that your directive knows about stuff that is outside of it (as in the {{message}} variable). That is a bad design and you should rework that. Also, in your fiddle you use the element[0].checked, while you can easily use the ng-modal value. But the ng-modal might not be there. Also, the input might not be a real checkbox, so your approach also fails. How to fix that? I'm gonna write you a demonstrative plunkr in a sec:
http://plnkr.co/edit/jyY6sQwXmtlGOvpdzETR?p=preview
angular.module('myApp', [])
.directive('box', [function(){
return {
restrict: 'E',
scope: {
ngModel: '='
},
templateUrl: 'box.tpl.html',
replace: true
};
}])
.controller('MyController', ['$scope', function MyController($scope){
$scope.checkboxValue = true;
}]);
What we did here is make sure our directive is always a checkbox by providing the checkbox inside the template. The you are able to do styling etc, of your directive.

Related

Angular JS - ng-change not getting triggered

I have created a directive for showing a X besides a text box for clearing the data inside the text box,
Directive JS
angular.module(appName).directive('clrTxt', function () {
return {
restrict: 'E',
replace: true,
scope: {
cntrlas: '=',
mdlval: '='
},
link: function (scope, elem, attrs, ctrl) {
scope.cleartxt = function () {
scope.cntrlas[scope.mdlval] = '';
}
},
template: '<button class="close-icon" type="reset" id="closeicon" ng-click="cleartxt()" ><img src="/resources/img/quote-tool-close.png" class="clear-icon"></button>'
};
});
HTML
<input type="text" ng-model="item.epinNumber" ng-change="numberLengthCheck(item)" >
<clr-txt cntrlas="item" mdlval="'epinNumber'"></clr-txt>
This will create a X icon at the end of the text box and will clear the data when you click on it.
The issue is, I'm triggering a function on-change , So when the X icon is clicked, the data will be cleared and so ideally. the change event should be triggered. But for some reason the change event is not triggered when the data is cleared using the X directive.
The key point is to set bindToControllerand controllerAs alias.then you can access the controller function inside link function of directive.
angular.module('plunker', []);
function MainCtrl($scope) {
$scope.name = 'Test';
$scope.numberLengthCheck = function(n){
alert('change triggered '+ n);
};
}
angular.module('plunker').directive('clrTxt', function(){
return {
restrict: 'E',
controller: 'MainCtrl',
controllerAs: 'vm',
bindToController: true,
scope: {
cntrlas: '='
},
templateUrl: 'reverse_template.html',
replace: true,
link: function(scope, elem, attr, ctrls) {
scope.cleartxt = function () {
scope.cntrlas = '';
scope.numberLengthCheck(scope.cntrlas);
}
}
};
});
In ng-change the expression is not evaluated when the value change is coming from the model. If you want to listen the model $watch is useful
And here I think no need of sending extra attributes (cntrlas,mdlval) to clrTxtdirective. You can access the parent scope in directive by making scope:false which is default.
Directive JS
app.directive('clrTxt', function () {
return {
restrict: 'E',
replace: true,
template: '<button type="reset" ng-click="cleartxt()" >X</button>',
link: function (scope, elem, attrs, ctrl) {
scope.cleartxt = function () {
scope.item.epinNumber = '';
}
}
};
});
Controller JS
app.controller('MainCtrl', function($scope) {
$scope.$watch('item.epinNumber', function(newvalue,oldvalue) {
console.log('new value is ='+newvalue+ ' and old value is ='+oldvalue);
});
});
HTML
<body ng-controller="MainCtrl">
<input type="text" ng-model="item.epinNumber">
<clr-txt ></clr-txt>
{{item.epinNumber}}
</body>
Here is the working plunker LINK

Change text value when button is pressed with directives in angular

Here's my fiddle
I basically want to be able to change the text when a button is pressed. I have tried with both $observe and $watch inside link, but I still don't manage to get it working.
Code:
(function(){
angular.module('app', [])
.directive('testDirective', function(){
return {
restrict: 'E',
scope: {
title: '#'
},
template: '<div>this is a {{ title }}</div>',
link: function(scope, element, attrs) {
//?
}
};
});
})()
You need to pass data as scope variable, you should not pass it as a string if you want to track changes.
check this fiddle, replace counter data with your desired data. Hope this helps
<div ng-controller='myctrl'>
<test-directive title="counter"></test-directive>
<hr></hr>
<button type="button" ng-click = 'onclickbutton()'>Change names</button>
</div>
(function(){
angular.module('app', [])
.controller('myctrl',function($scope){
$scope.counter = 0;
$scope.onclickbutton = function(){
$scope.counter++;
}
})
.directive('testDirective', function(){
return {
restrict: 'E',
scope: {
title: '='
},
template: '<div>this is a {{ title }}</div>',
link: function(scope, element, attrs) {
}
};
});
})();

Transcluded input ng-model does not update scope variable passed to directive

I have a directive that is essentially a complicated label tag that transcludes an input element and takes the input box's ng-model as a parameter.
<div switch model="testObject.switch">
<input type="checkbox" ng-model="$parent.testObject.switch">
</div>
Prior to upgrading to angular 1.4, clicking on the directive updated the ng-model and triggered the watch inside the directive.
Now, clicking on the directive still affects the input box, but the value inside the directive is unaffected.
I would appreciate any insight into what caused this change and how to fix it.
fiddle
I have updated your fiddle with working code. If you require ngModel in your directive and watch its $modelValue, you are able to get the behavior that you are looking for.
HTML:
<div switch ng-model="testObject.switch">
Directive:
booleanSwitchModule.directive('switch', [function () {
return {
scope: {},
require: "?^ngModel",
link: function (scope, elem, attr, ngModel) {
var timesChanged = 0;
scope.$watch(function() {return ngModel.$modelValue; }, function (val) {
if (val != undefined) {
alert("model changed " + ++timesChanged + " times");
scope.switchPosition = scope.model;
}
});
},
restrict: 'EA',
replace: true,
transclude: true,
template: '<label class="switch">' +
'directive scope model: {{ngModel}}' +
'<span ng-transclude></span>' +
'</label>',
}
}]);
Here is the updated fiddle: https://jsfiddle.net/62911kx5/3/

How to pass data from directive to controller?

I am currently working on integrating a plugin in my angular application and I'm trying to convert it into a directive. I have fiddled around with the events and methods of the plugin but failed to get the desired results. here's my code:
HTML
<div class="input-daterange datepicker full" rangepicker ng-model="packer.selected.date">
<i class="fa fa-calendar"></i>
<div class="inputs datepicker">
<input
ng-model="packer.selected.date.start"
name="start"
value="<% packer.initData.dateStart %>">
<span class="add-on">-</span>
<input
ng-model="packer.selected.date.end"
name="end"
value="<% packer.initData.dateStart %>">
</div>
</div>
Javascript:
Application.directive('rangepicker', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, ngModel) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
/*
$(element).find('input').each(function(){
$(this).trigger('input');
}) */
})
}
};
});
Application.controller('PackingStatisticsController', ['$scope', '$http', 'initData', function($scope, $http, initData) {
var packer = this;
packer.initData = initData;
packer.selected = {
date : {
start : "",
end : ""
},
user : ""
}
packer.log = function()
{
console.log(packer.selected);
}
}]);
I've read anything I thought was relevant to my issue but I haven't managed to shed the veil of confusion. The commented code is supposed to trigger the input value change event which I hoped would update the model. I fail to understand where the model I designate in the html meets my directive's data.
https://bootstrap-datepicker.readthedocs.org/en/latest/index.html this is the plugin I'm working with.
you can pass as an attribute the model you want to modify from the controller's scope like this (look at the scope property):
Application.directive('rangepicker', function() {
return {
restrict: 'A',
scope : {
model : '='
}
link: function(scope, element, attrs, ngModel) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
/*
$(element).find('input').each(function(){
$(this).trigger('input');
}) */
})
}
};
});
and in the html:
<div class="input-daterange datepicker full" model="myModelVar" rangepicker ng-model="packer.selected.date">
now the myModeVar is two way data bindable. once you change it in the directive it changes in the controller's scope.
in the controller:
Application.controller('PackingStatisticsController', ['$scope', '$http', 'initData', function($scope, $http, initData) {
$scope.myModelVar = ...;
}]);
Alas, i have done it!
Application.directive('rangepicker', function() {
return {
restrict: 'A',
require: 'ngModel', // added the ngmodel requirement
scope : {
ngModel: "="
}, // also added this, which if i understood well
// makes the 2 way data binding possible
link: function(scope, element, attrs) {
$(element).datepicker({
format: 'yyyy-mm-dd'
});
$(element).on('changeDate', function(){
var values = $(element).find('input');
var interval = {
start : values[0].value,
end : values[1].value
}
scope.$apply(function(){
// and here i update the given model scope (packer.selected.data)
scope.ngModel = interval;
});
})
}
};
});

AngularJS: Binding inside directives

I'm trying to acheive databinding to a value returned from a service inside a directive.
I have it working, but I'm jumping through hoops, and I suspect there's a better way.
For example:
<img my-avatar>
Which is a directive synonymous to:
<img src="{{user.avatarUrl}}" class="avatar">
Where user is:
$scope.user = CurrentUserService.getCurrentUser();
Here's the directive I'm using to get this to work:
.directive('myAvatar', function(CurrentUser) {
return {
link: function(scope, elm, attrs) {
scope.user = CurrentUser.getCurrentUser();
// Use a function to watch if the username changes,
// since it appears that $scope.$watch('user.username') isn't working
var watchUserName = function(scope) {
return scope.user.username;
};
scope.$watch(watchUserName, function (newUserName,oldUserName, scope) {
elm.attr('src',CurrentUser.getCurrentUser().avatarUrl);
}, true);
elm.attr('class','avatar');
}
};
Is there a more succinct, 'angular' way to achieve the same outcome?
How about this ? plunker
The main idea of your directive is like
.directive('myAvatar', function (CurrentUserService) {
"use strict";
return {
restrict: 'A',
replace: true,
template: '<img class="avatar" ng-src="{{url}}" alt="{{url}}" title="{{url}}"> ',
controller: function ($scope, CurrentUserService) {
$scope.url = CurrentUserService.getCurrentUser().avatarUrl;
}
};
});

Resources