I have a form and some fields get disabled with various conditions. Is there a way to determine is a model disabled (without running the same ng-disabled condition in the controller)?
If you have a form in your view, there would be a form object with the name you specified for the form on your controller scope which you can access inside your controller.
However you cannot access attributes (disabled is an attribute on your input) that from controller level (vs you can easily access input attributes from a directive):
app.directive('mydir', function ($compile) {
return {
require: '^form',
link: function(scope, element, attrs, formCtrl) {
var allDisabledInputs = $(formCtrl).find(':input:disabled');
//do stuff here
}
};
});
And on your form:
<form name="someForm" my-dir >
<input name="input1" />
<input name="input2" disabled />
</form>
Related
I have a text input within a directive that I want to use as a filter for displaying a list of items, but I don't want the entering of a filter to affect the containing forms $pristine value so that entering a filter doesn't enable the save and show the Reset. How do I do this in angularJS (1.6.x)?
directive template
<form name='myForm'>
<input placeholder="Filter" class='form-control' type='text' ng-model='vm.searchText'>
<ul><li ng-repeat='item in vm.list | filter:vm.searchText'/></ul>
<div>
<br>
<button class='btn btn-primary' ng-click='vm.save()' ng-disabled="myForm.$pristine || frmCrm.$invalid">Save</button>
<div class='pull-right'>
<button class='btn btn-warning' ng-click="vm.reset()" ng-hide="myForm.$pristine">Reset</button>
</div>
</div>
</form>
Yes I know I could easily put the filter input outside the form in this example, but in my actual situation that isn't feasible as I have nested forms and one that wraps basically the whole page.
here's plnkr example:
http://plnkr.co/edit/y1dJLPbyvlZuIW1f7ey9
You can override the $setDirty and $setPristine methods of the ngModel:
angular.module('xyz').directive('dontCheck', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
//set to empty functions
ngModelCtrl.$setPristine = angular.noop;
ngModelCtrl.$setDirty = angular.noop;
}
}
});
I've forked your plunker, you can try the solution:
http://plnkr.co/edit/6UVTQjJtwu4mOXVt7sPT?p=preview
Edit:
Updated plunker to follow your code style. I will leave the code here as is.
I would like my form labels to display a red asterisk next to the label when the corresponding form control has a required attribute.
Instead of hard coding the asterisk, I desire a way to append the asterisk to the label dynamically during page load if the label's corresponding input, select or textarea is required (the element the label corresponds to).
I created the directive below, and the directive works. But is there a better, more native way to accomplish my goal? This directive finds all the div.form-group containers and adds a red * character after the label if the corresponding form control inside the div.form-group has a required attribute.
myApp.directive('labelsRequired',function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs){
elem.find('div.form-group').each(function(i, formGroup){
var formControls = $(formGroup).find('input, select, textarea');
console.log(formControls)
if (0 !== formControls.length && undefined !== $(formControls[0]).attr('required')){
jLabel = $(formGroup).find('label');
jLabel.html(jLabel.html()+ "<span class='red-color'>*</span>");
}
})
}
}
});
The directive assumes all inputs, selects, and textareas are inside a div.form-group container.
<div class='form-group'>
<label>First Name</label><!-- this label gets an asterisk -->
<input name='fname' required />
</div>
<div class='form-group'>
<label>Favorite Food</label><!-- this label does not get an asterisk -->
<input name='favFood' />
</div>
You don't need a directive, there are built-in form properties you can use with filters like ng-show, look:
<div ng-app="myApp" ng-controller="myCtrl">
<form name="userForm" novalidate>
<div class='form-group'>
<label>First Name</label>
<input name='fname' ng-model="fname" required />
<label ng-show="userForm.fname.$dirty && userForm.fname.$error.required">* Required field</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
If you define an ng-model for the input you can deal with it looking if it is filled or not. You can also check it only after the user "dirty" it with userForm.fname.$dirty, so the label will be shown only after a user try to input something but then clear it. Try playing with it here JSFiddle
Building off of Corey's answer:
I just used compile rather than link, as I saw that my required attribute was not being applied to my input elements. I also included a select tag for any dropdowns that I had.
app.directive('inputRequired', function () {
return {
restrict: 'A',
compile: function (elem) {
elem.find('label').append("<sup><i class='fa fa-asterisk'></i></sup>");
elem.find('input').attr('required', 'required');
elem.find('select').attr('required', 'required');
}
};
});
If you're not using the built-in Angular validation, you could restructure your directive and attach it to your .form-group element. Like this:
app.directive('inputRequired', function() {
return {
restrict: 'A',
link: function(scope, elem, attr) {
elem.find('label').append('<span>*</span>');
elem.find('input').attr('required', 'required');
}
};
});
Your HTML would then look like:
<div class="form-group" input-required>
<label>Name</label>
<input name="name" />
</div>
<div class="form-group">
<label>Food</label>
<input name="food" />
</div>
However, if you haven't looked into the built-in validation with Angular, I would recommend using it.
This might come too late and it might not be too elegant but it works, if anyone needs it:
<label ng-show="userForm.fname.$validators.hasOwnProperty('required')">* Required field</label>
I want to have an input with text that is disabled but also selects all when you click on it.
Here's a working plnkr
http://plnkr.co/edit/o2hu8MCU2bjVPPFhhBLx?p=info
Here's my directive:
app.directive('selectAll', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('click', function () {
this.select();
});
}
};
})
and the html:
<input type="text" disabled select-All size="60" value="http://google.com"> - directive with disabled
<br>
<input type="text" select-All size="60" value="http://google.com"> - directive without disabled
I would like for the input to not be editable but still allow for the select all directive to work.
I tried adding the disabled functionality to the directive but I sometimes use this directive for thing other than input as well.
Any guidance?
so, garuuk, use readonly instead of disabled and just style your input to look disabled. Maybe slightly opaque
Is there something special going on with input type="email" and ng-model attribute?
If the input is email, then the model doesnt update.
If I change the input type to text, number or date it updates correctly.
Bug or some special magic email validation behavior that I dont understand?
It does some validation on then input, so you need to have entered a valid email address before it is bound to the model.
This is the regex that is used:
/^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/
Basically you need to input an address that is at least a#b.co
It's not a bug, it's only update when we type correct email address format for email validation.
Add this attribute ng-model-options="{'allowInvalid': true}" to allow invalid email input.
A complement, you can use properties on you form to see if your email is valid, like this :
HTML:
<form name="myForm" ng-submit="submit()">
<input type="email" ng-model="email1" name="email1" />
</form>
Javascript:
//[formName].[inputFieldName].property
myForm.email1.$pristine;
// Boolean. True if the user has not yet modified the form.
myForm.email1.$dirty
// Boolean. True if the user has already modified the form.
myForm.email1.$valid
// Boolean.True if the the form passes the validation.
myForm.email1.$invalid
// Boolean. True if the the form doesn't pass the validation.
myForm.email1.$error
Reference
Starting from Angular 1.3, you can easily overwrite the 'email' validator and make it always return true.
angular
.module('myApp', [])
.controller('MainController', function() {
this.email = '';
})
.directive('noEmailValidation', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attr, ctrl) {
ctrl.$validators['email'] = function() {
return true;
};
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<div ng-app="myApp">
<form ng-controller="MainController as main">
<div>Email: {{main.email}}</div>
<input type="email" ng-model="main.email" no-email-validation>
</form>
</div>
Enjoy.
I coded the following in my form:
<td><input type="text" ng-model="row.title" /></td>
When I look at my DOM with Chrome developer tools I see the following:
<input type="text" ng-model="row.title" class="ng-pristine ng-valid">
How can I make it so that when there is a change made to the input that the input has a class added to it?
There are two good ways to approach this problem:
1. Use the built-in ng-dirty class that Angular puts on the element.
When you change an input managed by Angular, it adds some CSS classes to the input for various states. These include:
ng-pristine - the input has not been modified
ng-dirty - the input has been modified
So, if you can modify your CSS to be based off the .ng-dirty class, you're good to go.
2. Use a form directive with the $dirty flag.
When you use a form element, Angular assigns a FormController instance on the scope with the same name as the name attribute on the form; each input inside the form gets attached to that FormController instance as a property, again with the same name as the name attribute on the input. For example,
<form name="myForm">
<input type="text" name="myInput">
</form>
gives you
$scope.myForm.myInput
Each input property has some of its own properties on it, including $pristine and $dirty; these work just like the CSS classes listed above. Thus, you can check for the $dirty flag on the input and use ng-class to conditionally apply a class to the element. An example:
<div ng-controller="MainController">
<form name="myForm">
<input name="myInput" ng-model="model" ng-maxlength="3"
ng-class="{changed: myForm.myInput.$dirty}">
</form>
</div>
You can find a working example here: http://jsfiddle.net/BinaryMuse/BDB5b/
Take a look at this jsfiddle: http://jsfiddle.net/hNrEV/2/
The main idea is using $scope.$watch to watch for changes to the input box. I gave it an id of rowTitle, and used a directive called watchRowTitle that watches for changes to $scope.row.title, and adds a class 'red' that colors the text red whenever the text in the input box is equal to 'wrong title'.
It is probably good practice to do DOM manipulation in directives. Here, the watchRowTitle directive returns an object with 4 keys:
template - the html that replaces the watch-row-title tag. we dont need this here
scope - Here we make use of an isolated scope. Basically, the '=' establishes a 2-way data binding between between scope.title inside the watch-row-title directive and the $scope.row.title value inside the MyCtrl controller.
restrict - We give it a value of E, which stands for element. So this restricts the use of the watch-row-title directive within html tags, in other words: <watch-row-title></watch-row-title>
link - this is the link function, where the interesting stuff happens. In here, we use scope.$watch on title. We have to supply a function with 2 parameters newValue and oldValue (you can name them to something else, but naming them this way is more meaningful), that holds the new and old values of the variable being watched. Whenever the scope.title variable becomes the string 'wrong title', it adds the CSS class 'red' to the input box with id rowTitle (notice how the text in the input box turns red). Otherwise, it removes that CSS class. This portion is done using JQuery.
HTML:
<div ng-app="myApp" ng-controller="MyCtrl">
<input id="rowTitle" type="text" ng-model="row.title" class="ng-pristine ng-valid" />
<watch-row-title title="row.title"></watch-row-title>
</div>
CSS:
.red {
color: red;
}
JavaScript:
angular.module('myApp', [])
.controller('MyCtrl', [
'$scope',
function ($scope) {
$scope.row = {};
}
])
.directive('watchRowTitle', [
function () {
return {
template: '',
scope: {
title: '='
},
restrict: 'E',
link: function(scope, element, attr) {
scope.$watch('title', function(newValue, oldValue) {
if (newValue === 'wrong title') {
$('#rowTitle').addClass('red');
} else {
$('#rowTitle').removeClass('red');
}
});
}
};
}
]);
HTML
<input type="text" id="inputTitle" ng-model="row.title" />
JS
$scope.$watch('row.title', function(newValue) {
// Add CSS class on input
$('#inputTitle').addClass('YourCSSClass');
}, true);