I am trying to use angular's form validation from inside a templateUrl.
I have a directive that loads a templateUrl in which i have a form with inputs that get ng-required and ng-regex values from directive scope. Now, i tried to put in my directive's scope
form: '=', but when i access scope.form it is undefined.
I must specify that my submit button is outside of the form, and when clicked ng-click='save($index)' it must first check that the form is valid and then proceed with saving the edited data. scope.save() is defined in my directive.
this is from template:
<tr data-ng-repeat="row in source.data " data-ng-class="{'selected':row.$_selected}" >
<td data-ng-repeat="c in settings.columns" data-ng-click="toggleSelect(row)" >
<form name="editForm" id="editForm" novalidate>
<div ng-switch on="c.type" ng-show="editMode[$parent.$index]">
<span ng-switch-when="text" >
<input type="{{c.type}}" data-ng-model="row[c.name]" ng-required="{{c.isRequired}}" ng-pattern="{{c.regex}}"/>
</span>
<span ng-switch-when="select" >
<select data-ng-model="row[c.name]" ng-selected="row[c.name]" ng-init="row[c.name]" ng-options="item.value as item.name for item in c.items" ng-required="{{c.isRequired}}">
<!--<option data-ng-repeat="(value, name) in c.items" value="{{value}}">{{name}}</option>-->
</select>
</span>
<span ng-switch-when="textarea">
<textarea ng-model="row[c.name]" ng-required="{{c.isRequired}}" ng-pattern="{{c.regex}}">
</textarea>
</span>
<span ng-switch-when="checkbox">
<!--<label for="checkboxInput">{{c.name}}</label>-->
<input name="checkboxInput" type="checkbox" data-ng-model="row[c.name]" ng-true-value="{{c.true}}" ng-false-value="{{c.false}}"/>
</span>
<span ng-switch-default="">
{{row[c.name]}}
</span>
</div>
</form>
<span ng-hide='editMode[$parent.$index]'>{{row[c.name]}}</span>
</td>
<td>
<a href="{{row[settings.specialFields.url]}}" class="btn btn-default opacity75" data-ng-if="row[settings.specialFields.url]">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
</td>
<td data-ng-if="row[settings.specialFields.isEditable]">
<button ng-click="edit(row)" ng-show="!editMode[$index]" class="btn btn-primary" >
edit {{$index}}
</button>
<button ng-click="save($index)" ng-disabled="" ng-show="editMode[$index]" class="btn btn-primary">
save
</button>
<button ng-click="cancel($index)" ng-show="editMode[$index]" class="btn btn-default">
cancel
</button>
</td>
</tr>
and this is from my directive:
scope: {
settings: '=',
source: '=',
form: '='
},
templateUrl: function (element, attr) {
return attr.templateUrl || 'src/grid.html';
},
link: function (scope, element, attrs) {
scope.editMode = [];
scope.editing = false;
scope.previousData = {};
scope.edit = function(field) {
scope.editing = scope.source.data.indexOf(field);
scope.editMode[scope.editing] = true;
scope.previousData = angular.copy(field);
};
scope.save = function(index){
console.log(scope.form);
scope.editMode[index] = false;
if (scope.editing !== false ) {
//do the saving
scope.editing = false;
}
};
scope.cancel = function(index){
scope.editMode[index] = false;
if (scope.editing !== false) {
scope.source.data[scope.editing] = scope.previousData;
scope.editing = false;
}
}
Here you go:
Working Form
Form button is outside of form, and could also be outside of directive. It doesn't matter with Angularjs.
There are two inputs, both have required and both have regex validation as you stated.
There is a directive with a templateURL
The important thing to remember here is that the form must have a name, and then it is referenced by that name as in: scope.myForm when
You don't have to name the input fields as I did in the plunker, but if you do, and they are ALL different values from one another, then you can do this: scope.myForm.myInputName.$valid to see if each input is valid if you wished.
But, the actual form will not be valid until ALL the inputs are valid, so you probably just need to call valid on the form itself as in in the provided example.
If you move the button outside of the directive, you will have to move the submit function from the directive to the controller (most likely).
Let me know if this helps, I can change things if needed. Also, try the plunker first with your question, then post what's going on with that new code; just fork the plunker and provide a link.
Here is the plunker code just in case...
<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap#*" data-semver="3.2.0" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.css" />
<script data-require="jquery#*" data-semver="2.1.1" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script data-require="bootstrap#*" data-semver="3.2.0" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.js"></script>
<script data-require="angular.js#1.3.7" data-semver="1.3.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.7/angular.js"></script>
<script data-require="angular-ui-bootstrap#0.12.0" data-semver="0.12.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<my-directive></my-directive>
</div>
</div>
</html>
<form name="myForm" novalidate>
<label>Input #1 (required)</label><br>
<input ng-model="form.data.myName" name='myName' ng-pattern="/\D+/" ng-required="true" /> <span ng-show="myForm.myName.$error.pattern">Takes anything but digits</span><br>
<br>
<label>Input #2 (required)</label><br>
<input ng-model="form.data.myEmail" name='myEmail' ng-pattern="/\d+/" ng-required="true" /> <span ng-show="myForm.myEmail.$error.pattern">Takes only digits</span>
</form>
<br>
<p># I'm a button that is outside of the form!</p>
<p ng-model="form.submitted">Form Submitted: <span class="blue">{{ form.submitted }}</span></p>
<p>Form Valid?: <span class="blue">{{ myForm.$valid }}</span></p>
<button ng-click="submitForm('myForm')">Submit Form</button>
<p ng-model="form.data">Here is the Form data:</p>
{{ form.data }}
var app = angular.module('myApp', []);
app.controller('MyCtrl', function ($scope) {});
app.directive("myDirective", function () {
return {
restrict: 'AE',
require: '?ngModel',
templateUrl: 'my-directive.html',
link: function (scope, elm, attrs, ctrl) {
scope.form = {submitted: false, data: {}};
scope.submitForm = function(formname){
console.log(scope[formname].myEmail)
console.log(scope.formname)
console.log(scope[formname].$valid)
scope.form.submitted = true;
}
} // end link
} // end return
});
Related
I am facing this problem while selecting file and showing its name on span.
So basically I am trying that whenever I select some file I want to change the span Tag. This is my view
<form name="fileUploadForm" ng-submit="submitFile(fileUploadForm.$valid)">
<div class="form-group upload-btn-wrapper">
<input type="file" class="form-control" id ="myFileField" file-input="patient_file" required />
<button class="csv-upload-btn">choose file</button>
<span id="file-chosen">{{ patient_file }}</span>
</div>
<div class="form-group">
<input type="submit" value="Upload" class="btn btn-primary">
</div>
</form>
following is my directive code
(function() {
angular.module('practimyze.dashboard')
.directive('csvUploader', ["$parse", function($parse) {
return {
restrict: 'EA',
templateUrl: "dashboard/breakdown/csv-uploader/csv-uploader.tpl.html",
replace: true,
controller: function($scope, valueHandler, Auth, $rootScope, blockUi, DashboardApi, fileUploadService, apiLinks, toaster){
},
link: function(scope, element, attrs) {
var model = $parse(attrs.csvUploader);
var modelSetter = model.assign;
element.bind('change', function() {
scope.$apply(function() {
modelSetter(scope, element[0].patient_file);
});
});
}
};
}]);
})();
Initially I did this
$parse.assign(scop, element[0].files);
this was getting me error that parse has not function assign. Then I changed it to what i have written now it says (modelSetter is not a function).
I am not sure where i am doing wrong, Please help me out thanks
Edited:
this is the fiddle link, it seems to be working perfect there fiddle link
I am working on Angularjs form validation for multiple input values.
Below is the issue I am facing
Let's say I entered 'Johnss', directive will validate and give error Invalid name
click on plus(+) button adjacent to Name label to add new Name input.
Enter Peter. Now you will see "Invalid name" for both Peter and Johnss.
Click on cancel button next to Peter.
Now in place of 'Johnss' try to enter any value and no validation is being done. Whatever value you enter it is accepting and no validation is happening.
Same is the case with 'Place' label.
What is the better way of doing this validation on each of the element?. Can someone please help me on this.
Also I am looking for a way to disable + button when validation fails.
Below is the code I have written
JS
var app = angular.module('multipleInputs',[]);
app.controller('multipleInputsCtrl',function($scope) {
$scope.users = [
{}
];
$scope.places = [{}];
$scope.addUser = function() {
var newUser = {};
$scope.users.push(newUser);
}
$scope.removeUser = function(user) {
var index = $scope.users.indexOf(user);
$scope.users.splice(index,1);
}
$scope.addPlace = function() {
var newPlace = {};
$scope.places.push(newPlace);
}
$scope.removePlace = function(place) {
var index = $scope.places.indexOf(place);
$scope.places.splice(index,1);
}
});
app.directive('nameValidation', function() {
var acceptableNames = ['John', 'Sachin', 'Sam', 'Sudhir', 'Nalanda'];
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
mCtrl.$validators.name = function validationError(value) {
if (value && acceptableNames.includes(value)) {
return true;
} else if (value && !acceptableNames.includes(value)) {
return false;
} else {
return true;
}
}
}
};
})
app.directive('placeValidation', function() {
var acceptableNames = ['Chicago', 'Houston', 'Dallas', 'Seattle', 'Mumbai'];
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
mCtrl.$validators.place = function validationError(value) {
if (value && acceptableNames.includes(value)) {
return true;
} else if (value && !acceptableNames.includes(value)) {
return false;
} else {
return true;
}
}
}
};
})
HTML
<!doctype html>
<html>
<head>
<title>Generate Multiple Input Fields Dynamically</title>
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.8/angular.min.js"></script>
<script src="multipleInputs.js"></script>
</head>
<body ng-app="multipleInputs" class="users-container" ng-controller="multipleInputsCtrl">
<form name="myForm">
<div>
<div class="form-group">
<label for="inputName" style="color:darkblue">Name <button type="button" class="btn btn-sm" ng-click="addUser()">+</button></label>
<section ng-repeat="user in users">
<input class="users-container-inputs-text" type="text" placeholder="First Name" ng-model="user.name" name="personName" name-validation>
<span class="users-container-inputs-button" ng-click="removeUser(user)">X
</span>
<span class="alert-danger" ng-show="myForm.personName.$error.name"> Invalid name</span>
</input>
</section>
</div>
<div class="form-group">
<label for="inputPlace" style="color:darkblue">Place <button type="button" class="btn btn-sm" ng-click="addPlace()">+</button></label>
<section ng-repeat="place in places">
<input class="users-container-inputs-text" type="text" placeholder="Place" ng-model="place.name" name="placeName" place-validation>
<span class="users-container-inputs-button" ng-click="removePlace(place)">X
</span>
<span class="alert-danger" ng-show="myForm.placeName.$error.place"> Invalid place</span>
</input>
</section>
</div>
</div>
</form>
</body>
</html>
Codepen link:
https://codepen.io/sudhir_rocks/pen/ZEpKVrR
You can use ng-disabled Directive in AngularJS to disable HTML elements.
https://docs.angularjs.org/api/ng/directive/ngDisabled
If the expression inside the ng-disabled attribute returns true then the form field will be disabled.
HTML Code :
<div id="createConfigManagementDivId"
class="panel-portal">
<h1 class="titlebar ng-binding">
GUI Configuration Data</h1>
<div class="grid span-20 centered vlead-2">
<ul class="tabs">
<li>
Boolean Data
<div class="content">
<h2> Boolean Data</h2>
<div>
<div ng-repeat="chk in boolchkbxs">
<label>{{chk.label}}</label>
<input type="checkbox" ng-change="GetBoolValue(chk)" ng-model="chk.Selected" />
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="text-right buttonbar">
<button id="applyButton">Apply</button>
<button id="updateCacheButton">Update GUI cache</button>
<button id="clearButton" ng-click="clearForm()" >Cancel</button>
</div>
<script type="text/javascript">
$(document).ready(function(){
});
</script>
Controller:
$scope.boolchkbxs = [{ label: "XXXX", Selected: true }];
$scope.GetBoolValue = function () {
var message = "";
for (var i = 0; i < $scope.boolchkbxs.length; i++) {
console.log($scope.boolchkbxs[i].Selected);
if ($scope.boolchkbxs[i].Selected) {
var lableName = $scope.boolchkbxs[i].label;
message += lableName + ",";
}
}
}
But I am unable to get the latest value modified by user in checkbox in the above code. $Scope.boolchkbxs[i].Selected always returns true (which is a default value);
Can someone please help me to solve the issue ??
Even I have tried samething for a single checkbox without ng-repeat, still the value it returns default and not the updated value.
The function GetBoolValue is not getting called when i change the value of checkbok
GetBoolValue should be called when change the chackbox value. i think this should be you expect. Now you can get the value of selected check box and find the label and calculate message.
The issue is due to setting the value of checkbox using jQuery. So ng-model value doesn't get updated.
To solve the issue, I have written checkboxWithChangeHandler in my controller (jsfiddle).
html
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-primary">
<input type="checkbox" ng-model="check"/> Default
</label>
<label class="btn btn-primary">
<input type="checkbox" ng-model="check" checkbox-with-change-handler/> Checkbox with change handler
</label>
</div>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-primary">
<input type="radio" name="options" ng-model="radio" value="onclick"/> Default
</label>
<label class="btn btn-primary">
<input type="radio" name="options" ng-model="radio" value="onchange" radio-with-change-handler/> Radio with change handler
</label>
</div>
<br/>
Checkbox: <span ng-bind="check"></span><br/>
Radio: <span ng-bind="radio"></span>
javascript:
var app = angular.module('app', []);
app.directive('checkboxWithChangeHandler', [function checkboxWithChangeHandler() {
return {
replace: false,
require: 'ngModel',
scope: false,
link: function (scope, element, attr, ngModelCtrl) {
$(element).change(function () {
scope.$apply(function () {
ngModelCtrl.$setViewValue(element[0].checked);
});
});
}
};
}]);
app.directive('radioWithChangeHandler', [function checkboxWithChangeHandler() {
return {
replace: false,
require: 'ngModel',
scope: false,
link: function (scope, element, attr, ngModelCtrl) {
$(element).change(function () {
if (element[0].checked) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(attr.value);
});
}
});
}
};
}]);
I am trying validate the textbox allow only integer number and greater than zero values.
Here I attached my code.
Html:
<body ng-app="myApp">
NUMBER ONLY <input type="text" allow-pattern="\d" />
</body>
Js:
var app = angular.module("myApp",[]);
app.directive('allowPattern', [allowPatternDirective]);
function allowPatternDirective() {
return {
restrict: "A",
compile: function(tElement, tAttrs) {
return function(scope, element, attrs) {
element.bind("keypress", function(event) {
var keyCode = event.which || event.keyCode; // I safely get the
if (!keyCodeChar.match(new RegExp(attrs.allowPattern, "i"))) {
event.preventDefault();
return false;
}
});
};
}
};
}
Also I tried like this bellow:
<body ng-app="myApp">
NUMBER ONLY <input type="text" allow-pattern="^[1-9][0-9]*$" />
</body>
But its not working.
Check jsfiddle link: click here
You can make use of angular form validation and also use ng-model-options
Here is the link to Codepen
Controller snippet :
var app = angular.module('app',[]);
app.controller('myCtrl',function($scope){
$scope.onlyNumbers = /^[1-9]+[0-9]*$/;
})
View :
<div ng-app="app">
<br />
<br />
<div class="col-md-2" ng-controller="myCtrl">
<form name="myForm1">
<div class="form-group" ng-class="{'has-error':myForm1.number1.$invalid}">
<label for="">Following validation happens as the user entrs a value.</label>
<input class="form-control" ng-pattern="onlyNumbers" ng-model="value1" name="number1"/> Valid? {{myForm1.number1.$valid}}
</div>
</form>
<form name="myForm2">
<div class="form-group" ng-class="{'has-error':myForm2.number2.$invalid}">
<label for="">Following validation happens after blur </label>
<input class="form-control" ng-pattern="onlyNumbers" ng-model="value2" name="number2" ng-model-options="{ updateOn: 'blur'}"/> Valid? {{myForm2.number2.$valid}}
</div>
</form>
For more on how you can better this process through controllers refer this link
What changes need to be made to get the code below to successfully call the same validation directive from multiple different forms using AngularJS? Note that the first form is able to validate correctly, in that it checks for an even number of open parens ( and close parens ) and alerts the user immediately if the number is not equal.
However, when I try to get the second form to share the first directive, the second form fails to have the parens validation. And when I create a duplicate directive with a different name and call the duplicate directive from the second form, the second form's validation disables submit when there are unequal numbers of open and close parens BUT FAILS TO NOTIFY THE USER.
It seems silly to have redundant directives when the same validation will be used on many forms in a site.
This might (or might not) be complicated by the fact that each form is in a different include that is exchanged in the same index.html in a single-page web application. To rule out this possibility, I am including all the code required to reproduce the problem as follows:
index.html is:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>iPlotmy</title>
<link rel="stylesheet" type="text/css" href="resources/css/my.css">
</head>
<body ng-app="myApp">
<!-- build:js({app,.tmp}) scripts/main.js -->
<script src="js/lib/angular.js"></script>
<script src="js/lib/angular-resource.js"></script>
<script src="js/lib/angular-ui-router.js"></script>
<script src="js/lib/angular-ui-router-statehelper.js"></script>
<script src="js/lib/angular-animate.js"></script>
<script src="js/lib/angular-cookies.js"></script>
<script src="js/lib/angular-storage.js"></script>
<script type="text/javascript" src="myController.js"></script>
<div ng-controller="myController">
<table width=100%>
<tr>
<td colspan=3 align=center>
<table>
<tr>
<th>
<button ng-click="firstLink()">first link</button>
</th>
<th>
<button ng-click="secondLink()">second link</button>
</th>
</table>
</td>
</tr>
<tr>
<td>
<div ng-if="linktype == 'first'">
<div ng-include="'index_firstinclude.html'"></div>
</div>
<div ng-if="linktype == 'second'">
<div ng-include="'index_secondinclude.html'"></div>
</div>
</td>
</tr>
</table>
</div>
</body>
</html>
The index_firstinclude.html is:
<table>
<tr>
<td width=200>
<form name="userForm" ng-submit="firstForm(userForm.$valid)" novalidate>
<input type="text" name="func1" ng-model="firstplot.func1" required data-countparens=""/>
<p ng-show="userForm.func1.$error.required && !userForm.func1.$pristine" class="help-block">Function is a required field.</p>
<p ng-show="userForm.func1.$error.countparens" class="help-block">The number of open parentheses ( does not equal the number of close parentheses ) !</p>
<br>
<button type="submit" ng-disabled="userForm.$invalid" >Click to Submit</button>
</form>
</td>
</tr>
</table>
The index_secondinclude.html is:
<table>
<tr>
<td width=200>
<form name="mysecondForm" ng-submit="secondForm(mysecondForm.$valid)" novalidate>
<input type="text" name="func1" ng-model="secondplot.func1" required data-countparens=""/>
<p ng-show="mysecondForm.func1.$error.required && !mysecondForm.func1.$pristine" class="help-block">Function is a required field.</p>
<p ng-show="mysecondForm.func1.$error.countparenssecond" class="help-block">The number of open parentheses ( does not equal the number of close parentheses ) !</p>
<br>
theta min: <input type="text" ng-model="secondplot.tmin"/>
<br>
theta max: <input type="text" ng-model="secondplot.tmax"/>
<button type="submit" ng-disabled="mysecondForm.$invalid" >Click to Submit</button>
</form>
</td>
</tr>
</table>
The relevant parts of the myController.js file:
// create angular app
var myApp = angular.module('myApp', []);
// create angular controller
myApp.controller('myController', ['$scope', '$http', function($scope, $http) {
$scope.linktype = 'home';
$scope.firstplot = {type:"firstplot", func1: 'some (test) value'};
$scope.secondplot = {type:"secondplot", func1: 'another (test) value'};
$scope.firstForm = function(isValid) {
// check to make sure the form is completely valid
if (isValid) {
var funcJSON = {type:"firstplot", func1: $scope.firstplot.func1};
$http.post('/path/to/first-server-url', funcJSON).then(function(response) {
$scope.firstplot = response.data;
});
}
};
$scope.secondForm = function(isValid) {
console.log("inside secondForm")
if (isValid) {
$scope.secondplot.uri = "resources/images/plot-second-equation.gif";
var funcJSON = {type:"secondplot", func1: $scope.secondplot.func1};
$http.post('/path/to/second-server-url', funcJSON).then(function(response) {
$scope.secondplot = response.data;
});
}
};
//below functions handle navigation links
$scope.firstLink = function() {
$scope.linktype = 'first';
}
$scope.secondLink = function() {
$scope.linktype = 'second';
}
}]);
/// below directive(s) for form validation
myApp.directive('countparens', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.countparens = function(modelValue, viewValue) {
return ctrl.$isEmpty(modelValue) ||
((modelValue.match(/\)/g) || []).length == (modelValue.match(/\(/g) || []).length);
};
}
};
});
myApp.directive('countparenssecond', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.countparenssecond = function(modelValue, viewValue) {
return ctrl.$isEmpty(modelValue) ||
((modelValue.match(/\)/g) || []).length == (modelValue.match(/\(/g) || []).length);
};
}
};
});