Angular: directive toggles disabled status overrides ng-disabled accidentally - angularjs

I am trying to create a directive called toggle-field that supports toggling disabled/enabled status for input/button fields in a form. The plnker is here. So far my directive works in a way that can disable the input and button that do not have ng-disabled directive. The problem is with the buttons that already have ng-disabled, for example:
<button type="submit" toggle-field class="btn btn-primary" ng-disabled="test_form.$invalid" ng-click="vm.submit()">Submit</button>
The link function in the directive is as following:
function toggleField($log, $compile) {
var directive = {
link: link,
require: '?ngModel',
restrict: 'A',
replace: true
};
var originalNgDisabledVal = '';
return directive;
function link(scope, element, attrs, ngModelCtrl) {
//enabled
if (attrs.hasOwnProperty('ngDisabled')) {
originalNgDisabledVal = attrs.ngDisabled;
$log.info('attrs.ngDisabled value: ' + scope.$eval(attrs.ngDisabled));
}
scope.$on('enabled', function() {
if (attrs.hasOwnProperty('ngDisabled')) {
element.show();
//attrs.$set('ngDisabled', originalNgDisabledVal); //not working
} else {
element.removeAttr('disabled');
}
});
//disabled
scope.$on('disabled', function() {
if (!attrs.hasOwnProperty('ngDisabled')) {
attrs.$set('disabled', 'disabled');
} else {
//attrs.$set('ngDisabled', true); //not working
element.hide();
}
});
$compile(element.contents())(scope);
} //link
}
As a workaround in the above code, I use the element.show() or element.hide() on fields that already have ng-disabled, but I would really like to make it work for disabled. Any help would be much appreciated.

You're going overkill doing this in the link function. You're link function should actually be pretty empty (completely empty in this case). Here's a plunkr with the solution:
http://plnkr.co/edit/KVeprA9qbqlkwvg9Bafx?p=preview
The link function is completely empty:
function link(scope, element, attrs, ngModelCtrl) {
//enabled
//$log.info('outherHTML: ' + element[0].outerHTML);
} //link
And then in the html you can pivot off enabled or not:
<input type="text" id="username" ng-disabled="!vm.enabled" toggle-field="" class="form-control col-sm-9" ng-model="vm.username" required="" />
<input type="password" id="password" ng-disabled="!vm.enabled" toggle-field="" class="form-control col-sm-9" ng-model="vm.password" />
<button type="submit" toggle-field="" class="btn btn-primary" ng-disabled="!vm.enabled || test_form.$invalid" ng-click="vm.submit()">Submit</button>
I don't even think you need a directive to handle doing this, you can do it just in the dom.

Related

Use ng-model with label element.

I am reading a code and got into a part where a label element is been used with data-ng-model is that possible ?
<label class="btn btn-success"
data-ng-model="myController.statusFilter"
data-btn-radio="'disabled'"
data-ng-click="myController.method()">
Disabled
</label>
It wont't work. Since ngModel should only be used with the inputs, since it involves two-way data binding.
Label does not deal with user input, thus it does not require the ngModel. So if you want to bind a scope variable to the label then you can use expressions.
Like
<label> {{labelText}} </label>
Note : you should define labelText in your controller, like $scope.labelText = "Hello"
Try to use ng-bind example in Plunker.
<label class="btn btn-success"
data-ng-bind="myController.statusFilter"
data-btn-radio="'disabled'"
data-ng-click="myController.method()">
Disabled
</label>
in this case ng-bind will work.
app.directive("editable", function ($document) {
return {
scope: {
text: "=ngModel"
},
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
element.text(scope.text);
scope.$watch('text', function (newValue, oldValue) {
if (newValue != oldValue) {
if (newValue != element.text())
element.text(newValue);
}
});
element.on("keyup", function (event) {
scope.text = element.text();
});
}
};
});
<label editable="true" contenteditable="true" ng-model="value"></label>
No, because it would not serve any purpose. What would the model affect? How would you affect the model when it was bound to the label?
If you are instead attempting to update the text inside the label, you should just put a variable in your template:
<label class="btn btn-success"
data-btn-radio="'disabled'"
data-ng-click="myController.method()">
{{ myController.statusFilter }}
</label>

angular- bind form submit with multiple buttons

I have a form with multiple submit buttons:
<form name="myForm" customSubmit>
<input type="text" ng-minlength="2">
<button type="submit" ng-click="foo1()"></button>
<button type="submit" ng-click="foo2()"></button>
</form>
and a directive:
angular.module('customSubmit', [])
.directive('customSubmit', function() {
return {
require: '^form',
scope: {
submit: '&'
},
link: function(scope, element, attrs, form) {
element.on('submit', function() {
scope.$apply(function() {
form.$submitted = true;
if (form.$valid) {
return scope.submit();
}
});
});
}
};
});
my goal is to submit the form only when it's valid, with multiple submit buttons (i.e. I can't use the ng-submit directive in the form). The above code doesn't work. What is the correct way to do that? Is that even possible?
I'd suggest you to use one of the simpler way of doing it. Just check your form is valid or not on ng-click & if its valid call the desired method from it.
Markup
<form name="myForm" customSubmit>
<input type="text" ng-minlength="2">
<button type="button" ng-click="myForm.$valid && foo1()"></button>
<button type="button" ng-click="myForm.$valid && foo2()"></button>
</form>
But checking myForm.$valid on each click looks bit repeating code in number of times. Rather than that you could have one method in controller scope which will validate form and call desired method for submitting form.
Markup
<form name="myForm" customSubmit>
<input type="text" ng-minlength="2">
<button type="button" ng-click="submit('foo1')"></button>
<button type="button" ng-click="submit('foo2')"></button>
</form>
Code
$scope.submit = function(methodName){
if($scope.myForm.$valid){
$scope[methodName]();
}
}
In both the cases you could make you button type to button instead
of submit
Update
To make it generic you need to put it on each button instead of putting directive on form once.
HTML
<form name="myForm">
<input type="text" name="test" ng-minlength="2" ng-model="test">
<button custom-submit type="submit" fire="foo1()">foo1</button>
<button custom-submit type="submit" fire="foo2()">foo2</button>
</form>
Code
angular.module("app", [])
.controller('Ctrl', Ctrl)
.directive('customSubmit', function() {
return {
require: '^form',
scope: {
fire: '&'
},
link: function(scope, element, attrs, form) {
element.on('click', function(e) {
scope.$apply(function() {
form.$submitted = true;
if (form.$valid) {
scope.fire()
}
});
});
}
};
});
Plunkr
The solution was to put the directive on the submit button, and use directive 'require':
<form>
<button my-form-submit="foo()"></button>
</form>
angular.module('myFormSubmit', [])
.directive('myFormSubmit', function() {
return {
require: '^form',
scope: {
callback: '&myFormSubmit'
},
link: function(scope, element, attrs, form) {
element.bind('click', function (e) {
if (form.$valid) {
scope.callback();
}
});
}
};
});

Get access to form controller validation errors in a custom directive

I have a directive that wraps a form element with some inputs. One of the options is passing in a formName. Usually, with a form with the example name of myForm, to show an error you would do something like myForm.firstName.$error.required.
But, how do I get access to the errors when the form name is dynamically being passed in to the directive?
example usage
<my-custom-form formName='myForm' formSubmit='parentCtrl.foo()'></my-custom-form>
directive
angular.module('example')
.directive('myCustomForm', [
function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'myCustomForm.directive.html',
scope: {
fornName: '#',
formSubmit: '&'
},
require: ['myCustomForm', 'form'],
link: function(scope, element, attrs, ctrls) {
var directiveCtrl = ctrls[0];
var formCtrl = ctrls[1];
scope.data = {};
scope.hasError = function(field) {
// how do i show the errors here?
};
scope.onSubmit = function() {
scope.formSubmit();
};
}
};
}]);
template
<form name="{{ formName }}" ng-submit="onSubmit()" novalidate>
<div class="form-group" ng-class="{'is-invalid': hasError('fullName') }">
<input type="text" name="fullName" ng-model="data.full_name" required />
<div ng-show="hasError('fullName')">
<p>How do I show this error?</p>
</div>
</div>
<div class="form-group" ng-class="{'is-invalid': hasError('email') }">
<input type="text" name="email" ng-model="data.email" ng-minlength="4" required />
<div ng-show="hasError('email')">
<p>How do I show this error?</p>
</div>
</div>
<button type="submit">Submit</button>
</form>
I think the only problem with your code is that the directive requires itself, I don't think that will work. Just removing the myCustomForm from the require works fine.
To check if the field has errors, you just need to check if the $error object in the form controller is empty.
require: ['form'],
link: function(scope, element, attrs, ctrls) {
var formCtrl = ctrls[0];
scope.data = {};
scope.hasError = function(field) {
// Field has errors if $error is not an empty object
return !angular.equals({}, formCtrl[field].$error);
};
Plunker

For IE 8 how to change border colour of text/input field if data entered is invalid using angular js?

I did tried http://plnkr.co/edit/A6gvyoXbBd2kfToPmiiA?p=preview but it is not working IE8. I need to change border colour of input field if data entered is not valid. and colour should get change after completion of data input(after pressing tab).
If feel your pain- we just got rid of IE7 support. These two things will make this work on IE8:
IE8 is very picky about valid HTML: you need to close the form tag
comment out "elm.unbind('input').unbind('keydown').unbind('change');"
My version of this:
.directive('checkEmailOnBlur', function($timeout){
var EMAIL_REGX = /^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attr, ctrl) {
if (attr.type === 'radio' || attr.type === 'checkbox') return;
elm.bind('blur', function() {
scope.$apply(function() {
if (EMAIL_REGX.test(elm.val())) {
ctrl.$setValidity('emails', true);
} else {
ctrl.$setValidity('emails', false);
}
});
});
}
};
});
<form name="myForm">
<div class="control-group" ng-class="{error: myForm.mail.$invalid}">
<label>mail</label>
<input type="text" name="mail" ng-model="user.mail" check-email-on-blur />
<span ng-show="myForm.mail.$error.emails" class="help-inline">Invalid email</span>
</div>
</form>

How do I keep a Angularjs text box in pristine state on initial load?

I know I am missing something simple but when I load my view for a new entity, any inputs marked with 'required' are validated immediately. This is before I touch any part of the form. I am using v1.2.0-RC1
Here is some code:
<form name="form" novalidate>
<input type="text" id="name" name="name" ng-model="item.name" required />
<span class="validation-error" ng-show="form.name.$error.required">*Required</span>
<button ng-click="post()">save</button>
</form>
Note: I add my form view via directive and use $compile.
MyApp.directive('loadForm', function($compile, $http){
return{
restrict: 'A',
replace: true,
link: function(scope, element, attr){
$scope.$watch(attr.loadForm, function(old, new){
if(old != new){
$http.get('myUrl').success(function(response){
var elem = angular.element(response);
var result = $compile(elem)(scope);
element.html(result);
});
}
});
}
};
});
Any ideas? Thanks.

Resources