I have this simple form with a text box that is required and a save button.
<form role="form" name="frmVariableConfig" novalidate ng-submit="frmVariableConfig.$valid && vm.saveChanges()">
<input type="text" ng-model="vm.CurrCustomer.Name" name="txtCustomerName" class="form-control input-sm validate[required]" placeholder="txtCustomerName" check-validation>
<button type="submit" class="btn btn-sm text-right">Save</button>
</form>
I'm using this directive to activate the Jquery Validation Engine
angular.module('app').directive('checkValidation', [
function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
element.closest('form').validationEngine(
{
promptPosition: 'centerRight',
scroll: true, prettySelect: true,
autoPositionUpdate: true,
//validateNonVisibleFields: true,
//autoHidePrompt: true,
autoHideDelay: 1000,
fadeDuration: 0.9
}
);
}
};
}]);
but it keeps on calling saveChanges() even if the text-box is empty. It should not call this function if the text-box is empty.
Please help.
In one project, the only way I found to solve this scenario was to pass the value of $valid to save method.
HTML
ng-submit="vm.saveChanges( frmVariableConfig.$valid )"
JS
$scope.saveChanges = function(valid) {
if(valid) {
//do save
}
}
Related
I'm trying to make use of the bootstrap-datepicker.
I have an existing AngularJS directive, but when setting the initial value, it does not update when making use of a date range.
HTML
<div class="input-group input-daterange" id="fromToDate" calendar ng-model="vm.fromToDate">
<input type="text" class="form-control input-sm" required ng-model="vm.bookingFromDate">
<span class="input-group-addon">to</span>
<input type="text" class="form-control input-sm" required ng-model="vm.bookingToDate">
</div>
Directive
// this directive updates the value, once it has been selected, but not when the initial value has been set**
function calendar() {
return {
require: 'ngModel',
link: function($scope, el, attr, ngModel) {
$(el)
.datepicker({
autoclose: true,
todayHighlight: true,
todayBtn: 'linked',
onSelect: function(dateText) {
$scope.$apply(function() {
ngModel.$setViewValue(dateText);
});
}
});
}
};
};
Then, I tried the following directive (found here), but this doesn't work either for a date range - instead:
function calendar() {
return {
require: '?ngModel',
restrict: 'A',
link: function ($scope, element, attrs, controller) {
var updateModel, onblur;
if (controller != null) {
updateModel = function (event) {
element.datepicker('hide');
element.blur();
};
onblur = function () {
var date = element.val();
return $scope.$apply(function () {
return controller.$setViewValue(date);
});
};
controller.$render = function() {
var date = controller.$viewValue;
element.datepicker().data().datepicker.date = date.from.toDate();
element.datepicker('setValue');
element.datepicker('update');
return controller.$viewValue;
};
}
return attrs.$observe('bdatepicker', function (value) {
var options = {
format: "yyyy/mm/dd",
todayBtn: "linked",
autoclose: true,
todayHighlight: true
};
return element.datepicker(options).on('changeDate', updateModel).on('blur', onblur);
});
}
};
};
Any assistance would be appreciated!
Thanks!
[Update]
CodePen to illustrate the issue:
<p data-height="322" data-theme-id="dark" data-slug-hash="BLkagb" data-default-tab="js,result" data-user="Programm3r" data-embed-version="2" class="codepen">See the Pen Bootstrap-Datepicker (Range) AngularJS by Richard (#Programm3r) on CodePen.</p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
You could use the following library to solve the issue. datepicker
Edit: to resolve disappearance of date.
<input type="text" class="form-control input-sm" ng-model="vm.bookingFromDate" id="fromDate">
<span class="input-group-addon">to </span>
<input type="text" class="form-control input-sm" ng-model="vm.bookingToDate" id="toDate">
in controller
$('#fromDate').val(vm.bookingFromDate);
$('#toDate').val(vm.bookingToDate);
I have made a directive for an Angular UI typeahead field. I am trying to design it so that as a user is typing, it sends asynchronous backend calls for results that will populate the dropdown that appears, as demonstrated in the Angular-bootstrap docs, example 2. However, when I start typing ('a' in this case), I get the error:
TypeError: Cannot use 'in' operator to search for 'getters' in a
Here is the factory method that makes the REST call:
certFactory.getUsersWithMatchingUsername = function(username) {
return $http.get(urlBase + '/managed/user?_queryFilter=userName+co+' + '\'' + username + '\'', {timeout: timeoutLength})
.then(function(response) {
// success
return response.data;
}, function(response) {
// error
return $q.reject(response);
});
};
Here is the controller method that calls the factory method:
$scope.getters = {
getUsersWithUsername: function (username) {
certFactory.getUsersWithMatchingUsername(username)
.then(function (response) {
var users = [];
angular.forEach(response.result, function(item) {
users.push(item);
})
return users;
}, function (error) {
console.log('failed!')
})
}
Here is my directive:
angular.module('myApp')
.directive('dropdownsearch', function(){
return {
restrict: 'E',
transclude: true,
scope: {
getterFn: '&',
config: '=', // object with properties label and type
disabled: '=?ngDisabled',
required: '=?ngRequred',
ngModel: '=',
options: '='
},
require: ['^form', 'ngModel'],
templateUrl: 'views/templates/dropdownSearchView.html',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
if (iAttrs.required !== undefined) {
// If attribute required exists
// ng-required takes a boolean
scope.required = true;
}
if (iAttrs.readonly !== undefined) {
// If attribute required exists
// ng-required takes a boolean
scope.readonly = true;
}
}
}
}
);
Here is the directive template:
<div class="form-group has-feedback">
<label class="control-label"> Choose {{ config.type }}></label>
<div class="dropdown dropdown">
<div class="input-group">
<input
type="text"
class="form-control"
placeholder="Make selection"
ng-model="ngModel"
uib-typeahead="option as option[config.label] for option in getterFn($viewValue)"
typeahead-editable="false"
ng-required="required"
ng-disabled="disabled"
/>
</div>
</div>
</div>
And finally, here is my directive in use:
<dropdownsearch ng-show= 'fieldMethods.showSubfield(subfield)'
getter-fn="getters.getUsersWithUsername"
ng-model="subsubfield.value"
config="fieldMethods.getConfig(subfield)">
</dropdownsearch>
Any help would be greatly appreciated. Also, let me know if any additional info is required.
The UI Bootstrap website Asynchronous Typeahead example uses uib-typeahead="address for address in getLocation($viewValue)". My guess is that your error message is saying that it's expecting for, not as in the directive. I don't understand this so I could be wrong! :-)
I have a directive (element) that I created for a "switch", that uses input[checkbox].
As long as the initial state of the checkbox is to be NOT checked, everything works fine. But if I want to set the initial state to checked (based on my controller value), then it's always the opposite. Why is the value not checked when the controller variable says it should be?
Html
<a-switch toggle-state="vm.doSomething"></a-switch>
Directive Html
<div class="switch-container">
<input type="checkbox" id="switch" />
<label for="switch" class="pm-switch"></label>
</div>
Javascript controller
vm.doSomething = {
state: true
};
Directive
angular.module('material')
.directive('aSwitch', [
'$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
toggleState: '=',
},
link: function (scope, element) {
element.on('click touchstart', function (event) {
if (event.srcElement && event.srcElement.id && event.srcElement.id === "switch") {
event.stopPropagation();
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
});
}
});
}
};
}
]);
I realize that in order to set the checked state of a generic
<input type="checkbox" />
I just need to add the attribute "checked", like
<input type="checkbox" checked />
but how do I do that if it's inside my directive's html?
As you are using isolated scope which has scope: { toggleState: '='} in it, you should have directly bind that toggleState.state to the input box inside template, so that the link function code will get remove as toggleState has been two way binded with your controller scope variable using toggle-state attribute
Directive.html
<div class="switch-container">
<input type="checkbox" id="switch" ng-model="toggleState.state"/>
<label for="switch" class="pm-switch"></label>
</div>
Is there a way to make a form invalid if two inputs don't match (like passwords), in Angular? Similar to form.password.$error.required?
pwd1:<input type="password" ng-model="pwd1" required /><br />
pwd2:<input type="password" ng-model="pwd" required /><br />
<div ng-show="pwd1 && pwd">Invalid:
<span ng-show="pwd1!==pwd">Wrong</span>
<span ng-show="pwd1===pwd">Correct</span>
</div>
This just checks if both the passwords are same.
Angular Form validation
Also check this Angular Ui which has password match directive
You can use Angular UI, it has a ui-validate directive:
<input name="password" required ng-model="password">
<input name="confirm_password" ui-validate=" '$value==password' " ui-validate-watch=" 'password' ">
Or, you can build your own directive for that
myApp.directive('matchPassword', function () {
return {
require: 'ngModel',
restrict: 'A',
scope: {
matchPassword: '='
},
link: function (scope, elem, attrs, ctrl) {
scope.$watch(function () {
return (ctrl.$pristine && angular.isUndefined(ctrl.$modelValue)) || scope.matchSenha === ctrl.$modelValue;
}, function (currentValue) {
ctrl.$setValidity('matchPassword', currentValue);
});
}
};
});
and use it like so:
<input required name="passwordConfirm" match-password="model.Password" />
I find this example step-by-step form with bootstrap and angularjs
How can I validate the email before jump to step 2 ??
or block the step jump until the fields are full??
function RegisterCtrl($scope, $location) {
$scope.steps = [
'Step 1: Team Info',
'Step 2: Campaign Info',
'Step 3: Campaign Media'
];
....some code
First, define your model in your controller:
function RegisterCtrl($scope, $location) {
$scope.step1 = {
name: '',
email: '',
password: '',
passwordc: ''
};
//...
Bind it to your form fields:
<input type="text" id="inputEmail" ng-model="step1.email" placeholder="Email">
Next, do your validation inside gotoStep():
$scope.goToStep = function(index) {
if (!$scope.step1.email.match(/[a-z0-9\-_]+#[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
return window.alert('Please specify a valid email');
}
//...
Obviously alert is not great so use jQuery to focus() and add the Bootstrap classes (control-group warning) to highlight the field.
http://jsfiddle.net/xayzP/
You should use directives to test your form field vadility, e.g:
app.directive('email', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (viewValue && viewValue.match(/[a-z0-9\-_]+#[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
// it is valid
ctrl.$setValidity('email', true);
return viewValue;
} else {
// it is invalid, return undefined (no model update)
ctrl.$setValidity('email', false);
return undefined;
}
});
}
};
});
In your html, you need to add the directive to your input field. You can show error messages if a field is not valid using the myForm.email.$error object:
<input type="text" name="email" id="inputEmail" placeholder="Email" ng-model="email" email required>
<span ng-show="myForm.email.$error.email" class="help-inline">Email invalid</span>
<span ng-show="myForm.email.$error.required" class="help-inline">Email required</span>
You can disable the next link until the form is valid by using myForm.$invalid on ng-class:
<li ng-class="{disabled: myForm.$invalid}" >
<a ng-model="next" ng-click="incrementStep(myForm)">Next Step →</a>
</li>
See example.