I wrote a directive for input focus & blur
angular
.module('app')
.directive('input', ['$filter', function($filter) {
return function(scope, element, attrs) {
if (element && element[0] && element[0].placeholder) {
scope.placeholder = element[0].placeholder;
element.bind("focus", function() {
console.log(scope.placeholder);
element[0].placeholder = "";
});
element.bind("blur", function() {
element[0].placeholder = $filter('translate')(scope.placeholder);
});
}
};
}]);
I want same functionality for text area also. But don't wanted to write an other directive. How I can do this?
var myDirective = ['$filter', function($filter) {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
if (element && element[0] && element[0].placeholder) {
scope.placeholder = element[0].placeholder;
element.bind("focus", function() {
console.log(scope.placeholder);
element[0].placeholder = "";
});
element.bind("blur", function() {
element[0].placeholder = $filter('translate')(scope.placeholder);
});
}
}
};
}]
angular
.module('app')
.directive('input', myDirective);
.directive('textarea', myDirective);
and in your html:
<input />
<textarea></textarea>
Related
In this plunk I have directive dir1 calling a method in directive dir2 as described here.
The problem is that the control object (scope.dir2Ctl) is empty in dir1 and I get TypeError: scope.dir2Ctl.call2 is not a function. Any ideas how to fix this?
HTML
<body ng-app="myModule" ng-controller="ctl">
<dir1 x1="1"></dir1>
</body>
Javascript
angular.module("myModule", [])
.controller('ctl', function($scope) {})
.directive('dir1', function ($timeout) {
return {
restrict: 'EA',
scope: {
x1: '='
},
template: '<p>x2 should be 2 = {{x2}} </p>' +
'<dir2 control="dir2Ctl"></dir2>',
link: function (scope, element, attrs) {
scope.dir2Ctl = {};
$timeout(function(){
console.log(scope.dir2Ctl)
scope.x2 = scope.dir2Ctl.call2();
},1000);
}
}
})
.directive('dir2', function () {
return {
restrict: 'EA',
scope: {
control: '='
},
template: '<p>some text in dir2</p>',
link: function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.call2 = function(){
return 2;
};
}
}
});
How do I use $broadcast to update custom validation in other directives which already have isolated scope?
I want to be able create separate validation rules used on a single input field.So in the future I can change the validation of a field by simply changing the directive reference.
Check the plunkr
edit: I am using angular 1.2.8
The element the directive is on has isolated scope.
Validation Directive 1
(function () {
'use strict';
angular
.module('app')
.directive('dateOneValidation', dateOneValidation);
function dateOneValidation() {
var directive = {
require: 'ngModel', // note: this has to stay
restrict: 'A',
link: link
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$on('updateDateOneValidation', function(e, date){
ctrl.$parsers.unshift(function (viewValue) {
var form = scope.form;
var dateOne = moment(form.dateOne.$viewValue, "DD/MM/YYYY", true);
var today = moment();
var dateOneBeforeOrOnToday = dateOne.isSame(today, 'day') || dateOne.isBefore(today, 'day');
dateOneBeforeOrOnToday ? form.dateOne.$setValidity('dateOneBeforeOrOnToday', true):
form.dateOne.$setValidity('dateOneBeforeOrOnToday', false);
return viewValue
});
});
}
}
})();
Validation Directive 2
(function () {
'use strict';
angular
.module('app')
.directive('dateTwoValidation', dateTwoValidation);
function dateTwoValidation() {
var directive = {
require: 'ngModel', // note: this has to stay
restrict: 'A',
link: link
};
return directive;
function link(scope, element, attrs, ctrl) {
scope.$on('updateDateTwoValidation', function(e, date){
ctrl.$parsers.unshift(function (viewValue) {
var form = scope.form;
var dateOne = moment(form.dateOne.$viewValue, "DD/MM/YYYY", true);
var dateTwo = moment(viewValue, "DD/MM/YYYY", true);
var dateTwoAfterDateOne = dateTwo.isSame(dateOne, 'day') || dateTwo.isAfter(dateOne, 'day');
dateTwoAfterDateOne ? form.dateTwo.$setValidity('dateTwoAfterDateOne', true):
form.dateTwo.$setValidity('dateTwoAfterDateOne', false);
return viewValue
});
});
}
}
})();
(function () {
'use strict';
angular
.module('app')
.directive('stepOne', stepOne);
function stepOne() {
parentController.$inject = ['$scope'];
function parentController($scope) {
var vm = this;
vm.dateOne = '01/01/2000'
vm.dateTwo = '01/01/1900'
vm.validateStepOne = validateStepOne;
function validateStepOne() {
$scope.$broadcast('updateDateOneValidation');
$scope.$broadcast('updateDateTwoValidation');
}
}
var directive = {
restrict: 'EA',
require: '^form',
templateUrl: 'src/app/form/step1.html',
scope: {
},
controller: parentController,
controllerAs: 'vm'
};
return directive;
}
})();
(function () {
'use strict';
angular
.module('app')
.directive('dateOneValidation', dateOneValidation);
function dateOneValidation() {
var directive = {
require: 'ngModel', // note: this has to stay
restrict: 'A',
link: link
};
return directive;
function link(scope, element, attrs, ctrl) {
var form = scope.form;
var today = moment();
scope.$watch(attrs.ngModel, function () {
validator()
});
scope.$on('updateDateOneValidation', function () {
validator();
});
function validator() {
var dateOne = moment(form.dateOne.$viewValue, "DD/MM/YYYY", true);
var dateOneBeforeOrOnToday = dateOne.isSame(today, 'day') || dateOne.isBefore(today, 'day');
dateOneBeforeOrOnToday ? form.dateOne.$setValidity('dateOneBeforeOrOnToday', true) :
form.dateOne.$setValidity('dateOneBeforeOrOnToday', false);
}
}
}
})();
(function () {
'use strict';
angular
.module('app')
.directive('dateTwoValidation', dateTwoValidation);
function dateTwoValidation() {
var directive = {
require: 'ngModel', // note: this has to stay
restrict: 'A',
link: link
};
return directive;
function link(scope, element, attrs, ctrl) {
var form = scope.form;
scope.$watch(attrs.ngModel, function () {
validator();
});
scope.$on('updateDateTwoValidation', function (e, date) {
validator();
});
function validator() {
var dateOne = moment(form.dateOne.$viewValue, "DD/MM/YYYY", true);
var dateTwo = moment(form.dateTwo.$viewValue, "DD/MM/YYYY", true);
var dateTwoAfterDateOne = dateTwo.isSame(dateOne, 'day') || dateTwo.isAfter(dateOne, 'day');
dateTwoAfterDateOne ? form.dateTwo.$setValidity('dateTwoAfterDateOne', true) :
form.dateTwo.$setValidity('dateTwoAfterDateOne', false);
};
};
}
})()
Alternatively You can use a higher shared scope with a form object and pass it to your directives. Something like the following:
topLevelScope - ngForm
directive1(topLevelScope.ngForm)
topLevelScope.ngForm.$setValidity('input1', true)
directive2(topLevelScope.ngForm)
topLevelScope.ngForm.$setValidity('input2', true)
directive3(topLevelScope.ngForm)
topLevelScope.ngForm.$setValidity('input3', true)
My 2 cents.
Have some directive to uncheck previously checked radio in group:
(function (angular, $) {
'use strict';
var radioGroupDirective = function () {
return {
restrict: 'EA',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModelController) {
var $radios = $element.find('input[type="radio"]');
$radios.click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
ngModelController.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
};
};
radioGroupDirective.$inject = [];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);
})(angular, $);
Usage:
<div radio-group ng-model="fruit">
<input type="radio" ng-model="fruit" value="Apple"/>
<input type="radio" ng-model="fruit" value="Banana"/>
<input type="radio" ng-model="fruit" value="Mango"/>
</div>
It works fine, but I want to remove duplicate code of ngModels in child inputs. Like this:
<div radio-group ng-model="fruit">
<input type="radio" value="Apple"/>
<input type="radio" value="Banana"/>
<input type="radio" value="Mango"/>
</div>
So I try to add ngModel to all child inputs dynamically at compile function
(function (angular, $) {
'use strict';
var radioGroupDirective = function ($compile) {
return {
restrict: 'EA',
require: 'ngModel',
link: function($scope, $element, $attrs, ngModelController) {
var $radios = $element.find('input[type="radio"]');
$radios.click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
ngModelController.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
compile: function (tElement, tAttrs) {
var $radios = tElement.find('input[type="radio"]');
angular.forEach($radios, function(radio) {
$(radio).attr('ng-model', tAttrs.ngModel);
});
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
$compile(iElement)(scope);
},
};
},
};
};
radioGroupDirective.$inject = ['$compile'];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);
})(angular, $);
but it causes an infinite compilation loop and a dead of the browser
You try to compile the entire the directive (radioGroup) again from the link function so it causes an infinite loop.
Instead compile only the inputs:
angular.forEach($radios, function(radio) {
$compile(radio)(scope);
});
See this plunker.
Full worked plunker for this directive (for someone who could find it usefull)
var radioGroupDirective = function ($compile) {
return {
restrict: 'EA',
require: 'ngModel',
compile: function (tElement, tAttrs) {
var $radios = tElement.find('input');
angular.forEach($radios, function(radio) {
$(radio).attr('ng-model', tAttrs.ngModel);
});
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
angular.forEach($radios, function(radio) {
$compile(radio)(scope);
});
$($radios).click(function($event) {
var $radio = $($event.target);
if ($radio.data('waschecked') == true) {
$radio.prop('checked', false);
$radio.data('waschecked', false);
controller.$setViewValue(null);
} else {
$radio.data('waschecked', true);
}
$radio.siblings('input[type="radio"]').data('waschecked', false);
});
},
};
},
};
};
radioGroupDirective.$inject = ['$compile'];
angular.module('radio.group', []).directive('radioGroup', radioGroupDirective);
I have two angularjs directives (extWindow and taskBar) and want to inject taskBar's controller into extWindow in order to access it's scope. Because they don't share the same scope I used
require : '^$directive'
syntax to include it.
Doing so I could get rid of the error 'Controller 'taskBar', required by directive 'extWindow', can't be found!' but TaskBarCtrl is still undefined in link(..) method of the extWindow directive.
Any suggestions how to fix it?
var mod = angular.module('ui', [])
.directive('taskBar', function() {
var link = function(scope, el, attrs) {
$(el).css('display', 'block');
$(scope.titles).each(function(i,t) {
el.append('<span>' + t + '</span>')
});
};
return {
scope: {},
restrict : 'E',
controller: function($scope, $element, $attrs) {
$scope.titles = [];
this.addTitle = function(title) {
$scope.titles.push(w);
};
this.removeTitle = function(title) {
$scope.titles = jQuery.grep(function(n,i) {
return title != n;
});
}
},
link: link
};
}).directive('extWindow', function() {
return {
scope: {},
require: '^?taskBar',
restrict: 'E',
transclude: true,
template: '<div class="ui-window">\
<div class="ui-window-header"><span>{{windowTitle}}</span><div class="ui-window-close" ng-click="close()">X</div></div>\
<div class="ui-window-content" ng-transclude></div>\
</div>',
link: function(scope, element, attrs, taskBarCtrl) {
scope.windowTitle = attrs['windowTitle'];
scope.close = function() {
$(element).css('display', 'none');
}
//taskBarCtrl is not recognized!!!
taskBarCtrl.addTitle(scope.windowTitle);
}
}
});
http://jsfiddle.net/wa9fs2nm/
Thank you.
golbie.
If you have a controller for your parent directive and you need something like.
this.scope = $scope;
this.attrs = $attrs;
And in your in you link function for the child you need something like
var Ctrl = ctrl || scope.$parent.tBarCtrl;
Here's a Plunker
I'm trying to pass a value to a directive. The directive is used to integrate a jquery plugin Knob
JSFIDDLE: http://jsfiddle.net/Tropicalista/TH87t/93/
I have this code:
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.number = 24;
})
App.directive('knob', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).knob().val(scope.number);
console.log(attrs)
}
};
});
The problem is that knob() doesn't return the element. Use this instead:
link: function(scope, element, attrs) {
$(element).val(scope.number).knob();
}
Here's your fiddle: http://jsfiddle.net/TH87t/94/
I used your question as reference for a fully bi-direction binding. For a working version with angular 1.2.1 see http://jsfiddle.net/sander_van_dam/m5YJu/
App.directive('knob', function() {
return {
require: 'ngModel',
scope: { model: '=ngModel' },
controller: function($scope, $element, $timeout) {
var el = $($element);
$scope.$watch('model', function(v) {
var el = $($element);
el.val(v).trigger('change');
});
},
link: function($scope, $element, $attrs,$ngModel) {
var el = $($element);
el.val($scope.value).knob(
{
'change' : function (v) {
$scope.$apply(function () {
$ngModel.$setViewValue(v);
});
}
}
);
}
}
});