How to use "required" attribute within Angular directive template? - angularjs

I'm using an AngularJS directive to generate two radio buttons. I'm hardcoding the "required" attribute into the directive template, but it doesn't behave as expected.
When no radio buttons are checked, it correctly displays this error message, as expected.
<p ng-show="form.$invalid">Error: the form is invalid.</p>
But it doesn't display this error message.
<p ng-show="form.gender.$invalid">Error: the gender input is invalid.</p>
Any idea why?
Please see the Plunker for details:
http://plnkr.co/edit/i5kVeX8WdrUbM83lT6O6?p=preview

You need to tell your directive to validate input fields initially. You can do this by defining $formatters callback checking validity of the fields:
app.directive('dRadio', function() {
return {
require: '^form',
restrict: 'E',
scope: { model: '=ngModel' },
template: '<input required type="radio" id="{{value}}" name="{{name}}" value="{{value}}" ng-model="model"><label for="{{value}}">{{label}}</label>',
link: function(scope, element, attrs, ngModelController) {
scope.name = attrs.name;
scope.value = attrs.value;
scope.label = attrs.label;
ngModelController[attrs.name].$formatters.unshift(function(value) {
ngModelController[attrs.name].$setValidity('required', !!scope.model);
return value;
});
}
};
});
Note, that you also need to add require: '^form' rule.
Demo: http://plnkr.co/edit/rac1RNNOWHs1wTnktJHE?p=preview

You can accomplish by doing this way
Plnkr
I changed the following line
<p ng-show="!test">Error: the gender input is invalid.</p>

Related

AngularJs directive change ngModel with delay & model's value don't match

Ng-change is acting weird in a directive. It seems to have a delay in the digest cycle resulting in the wrong (previous) ngModel value in controller immediately after the change.
ngModel : '='
https://codepen.io/anon/pen/moEgdG
What's going on and how to fix?
With the ng-model directive on a component, I recommend using one-way (<) for the input and $setViewValue on the output:
app.directive('newTag', function(){
return {
template: `
<input ng-model="test" ng-change="change(test)"> <br/>
{{test}}
`,
restrict: 'E',
require: "ngModel",
scope: {
ngModel : '<',
},
link: function (scope, elem, attrs, ngModel) {
scope.change = function(val) {
ngModel.$setViewValue(val);
};
},
};
})
Usage:
<new-tag ng-model="tagValue" ng-change("newTagUpdate(tagValue)")>
</new-tag>
For more information, see
AngularJS ngModelController API Reference
AngularJS Developer Guide - Implementing custom form controls (using ngModel)
AngularJS Comprehensive Directive API Reference - require

How to verify if a checkbox is checked with jqLite in AngularJs

I want to know if a checkbox is checked using jqLite in angularjs. I'm using a directive to check and verify if the value is true or false. I tried this code angular.element('#myCheckbox').val(), but the value returned was false.
<input type="checkbox" id='myCheckbox' my-directive>
angular.js
.directive('myDirective', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
console.log(angular.element('#myCheckbox').val());
},
};
})

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/

AngularJS - Formatting ngModel on custom datepicker directive

I'm using JQuery bootstrap datepicker (eternicode.github.io/bootstrap-datepicker/) in an angular application.
I've wrote my own directive to wrap this datepicker and do some date formatting. Datepicker works fine and display an appropriate date format.
Currently, I want to show a formatted date value and put a timestamp formatted value in ngModel.
My code is okay when I'm not trying to use template in my directive :
cosyApp.directive("datepicker", ['moment',
function(moment) {
function link($scope, element, attrs, ctrl) {
// Init JQuery datepicker
element.datepicker();
ctrl.$parsers.push(function(valueFromInput) {
// Format displayed value in timestamp format
return moment(valueFromInput).format('X');
});
}
return {
restrict: 'A',
require: 'ngModel',
link: link
};
}
]);
But when I use template attribute, ngModel and displayed value are the same :
return {
restrict: 'A',
replace: true,
require: 'ngModel',
template: "<div class='input-group date'> {{ngModel}}" +
"<input class='form-control' ng-model='ngModel'>" +
"<span class='input-group-addon'><i class='glyphicon glyphicon-calendar'></i></span>" +
"</div>",
link: link
};
I made a few changes to your plunker. I didn't try to figure out why the calendar is showing up right away, I instead just bound the datepicker to the input field.
Instead of trying to use ngModel on the input ng-mode directly as you were doing, I created an intermediate model object, then adding a function to watch for changes to the input and pass those changes to the ngModelController directly.
Side note, I believe if you plan on updating your model value outside the UI and want it to update the view, you will have to add a $watch to update the intermediate model object.
http://plnkr.co/edit/5213zUvnqyv0ARqc11aU?p=preview
cosyApp.directive("datepickerx",
function($window) {
function link($scope, element, attrs, ctrl) {
$scope.model = ctrl.$viewValue;
// Init JQuery datepicker
element.find('input').datepicker({
autoclose: true,
clearBtn: true,
});
$scope.changed = function() {
ctrl.$setViewValue($scope.model);
}
ctrl.$parsers.push(function(valueFromInput) {
// Format displayed value in timestamp format and store it to ngModel
return $window.moment(valueFromInput).format('X');
});
}
/* ********** This part of code doesn't works ********** */
return {
restrict: 'A',
replace: true,
require: 'ngModel',
scope: {
ngModel: '='
},
template: '<div class="input-group date">' +
'<input class="form-control" ng-model="model" ng-change="changed()"/>' +
'<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>' +
'</div>',
link: link
};
}
);

Dynamic ng-model binding inside a directive

I'm trying to create a custom component that uses a dynamic ng-model inside-out the directive.
As an example, I could invoke different components like:
<custom-dir ng-model="domainModel1"></custom-dir>
<custom-dir ng-model="domainModel2"></custom-dir>
With a directive like:
app.directive('customDir', function() {
return {
restrict: 'EA',
require: '^ngModel',
scope: {
ngModel: '=dirValue',
},
template: '<input ng-model="dirValue" />',
link: function(scope, element, attrs, ctrl) {
scope.dirValue = 'New';
}
};
});
The idea is that the textbox from the directive would change if the model changes, and in the other way around.
The thing is that I've tried different approaches with no success at all, you can check one of this here: http://plnkr.co/edit/7MzDJsP8ZJ59nASjz31g?p=preview In this example, I'm expecting to have the value 'New' in both of the inputs, since I'm changing the model from the directive and is a bi-directional bound (=). But somehow is not bound in the right way. :(
I will be really grateful if someone give some light on that. Thanks in advance!
Something like this?
http://jsfiddle.net/bateast/RJmhB/1/
HTML:
<body ng-app="test">
<my-dir ng-model="test"></my-dir>
<input type="text" ng-model="test"/>
</body>
JS:
angular.module('test', [])
.directive('myDir', function() {
return {
restrict: 'E',
scope: {
ngModel: '='
},
template: '<div><input type="text" ng-model="ngModel"></div>',
};
});

Resources