http://plnkr.co/edit/9Yq6aZHtKCuZMtPbjBIN?p=preview
I've tried and tried to get this to work but I've had no luck. I'm trying to disable the submit button using angularJS and the built in validation function. What I find is that when you first load the form, the submit button is active--not disabled!
I've tested and tried and I've found that my validation code accepts an empty string / null string in the Team Name, despite me requiring the input.
Does anyone know how to format this correctly? The ONLY way I've gotten it to work is to fudge the data with replacing an empty string with a single space...in production this is unacceptable...
Here is the index.html:
<body ng-controller="EnterController as enter">
<div class="panel panel-primary">
<div class="panel-body">
<form name="enterFormNew" ng-submit="enter.TeamNameNext()" autocomplete="off" novalidate>
<div class="row">
<div class="col-xs-8 col-md-6">
<div class="form-group">
<label for="teamname">Team Name</label>
<input name="TeamName" ng-required ng-minlength="2" ng-maxlength="40" ng-model="enter.Team.Name" type="text" id="teamname" class="form-control" />
<p ng-show="enterFormNew.TeamName.$touched && enterFormNew.TeamName.$invalid">This is not a valid team name.</p>
</div>
<div class="form-group">
<label for="division">Division</label>
<select name="selectDivision" ng-required ng-model="enter.Team.Division" id="division" class="form-control" ng-options="division.Name for division in enter.Divisions track by division.Id ">
<option value="">Select...</option>
</select>
<p ng-show="enterFormNew.selectDivision.$touched && enterFormNew.selectDivision.$invalid">A valid Division needs to be selected.</p>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-2">
<button type="submit" class="btn btn-primary" ng-disabled="enterFormNew.$invalid">Next</button>
</div>
</div>
</form>
</div>
</div>
</body>
And here is the app.js:
angular.module('Enter', [])
.controller("EnterController", [
function() {
this.RegistrationPhase = 0;
this.Divisions = [{ Id: 1,Name: "Normal"}, {Id: 2,Name: "Not Normal"}];
this.Team = { Name: "", ResumeKey: null, Division: { Id: -1 }, Players: []};
this.TeamNameNext = function() {
//Code removed
alert("Made it to the submit function!")
};
}
]);
the attribute ng-required requires a value. Don't get confused with HTML5 attribute required that doesn't require a value.
ng-required="true"
Because "Team.Division" is not undefined in your controller code.
If you could use the code below, you will get what you extected.
this.Team = { Name: "", ResumeKey: null, Division: undefined, Players: []};
You can use null or "" instead of undefined.
Related
I have an array of strings like:
questions: [
"Question 1?",
"Question 2?",
"Question 3?",
"Question 4?",
],
Then I have form fields in my data() like:
legitForm: {
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
},
Now, the problem I'm facing is when I fill inputs for any of questions
above the same field for other questions gets same value.
Here is my template code:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForm.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForm.duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForm.description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
And this is my post method that sends data to backend:
legitStore(question) {
this.legitForm.name = question; // set "name" in `legitForm` to value of `question` string
axios.post('/api/auth/userLegitsStore', this.legitForm, {
headers: {
Authorization: localStorage.getItem('access_token')
}
})
.then(res => {
// reset my data after success
this.legitForm = {
name: '',
answer: '',
description: '',
duration: '',
};
})
.catch(error => {
var errors = error.response.data;
let errorsHtml = '<ol>';
$.each(errors.errors,function (k,v) {
errorsHtml += '<li>'+ v + '</li>';
});
errorsHtml += '</ol>';
console.log(errorsHtml);
})
},
Here is issue screenshot:
Note: I've tried to
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
but I've got errors so i wasn't sure what I'm doing wrong, that's why
I'm asking here.
If you think of your questions as questions and answers, you can do something like this:
questions: [
{
question: 'Question 1',
answer: null,
description: null,
duration: null,
},
{
question: 'Question 2',
answer: null,
description: null,
duration: null,
},
]
Then when looping through your form, it would be more like this:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
And in the storing function you could send the data in the question instead of this.legitForm
Like you said you tried here:
change legitForm to array like legitForm: [], and legitForm: [{....}]
add index to my inputs v-model
You are supposed to be doing that.
I would change legitForm to:
//create legitForms equal to the length of questions
const legitForms = [];
for (i in questions) legitForms.push(
{
name: '', // this will be equal to question string
answer: '',
description: '',
duration: '',
}
);
and in template:
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
<div class="row">
<div class="col-md-12">
<p>
<strong>{{question}}</strong>
</p>
</div>
<div class="col-md-6">
<label for="answer">Answer</label>
<select class="mb-1 field" v-model="legitForms[index].answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-6">
<label for="duration">Duration</label>
<input class="field" v-model="legitForms[index].duration" type="text">
</div>
<div class="col-md-12">
<label for="description">Description</label>
<textarea style="height: 190px;" type="text" cols="5" rows="10" id="address" class="mb-1 field" v-model="legitForms[index].description"></textarea>
</div>
</div>
<button type="submit" class="saveButtonExperience float-right btn btn--custom">
<span class="text">Save</span>
</button>
</form>
</div>
<div v-for="(question, index) in questions" :key="index">
In your template you iterate through questions and within this tag render object legitForm it all questions will refer to the same 1 object that's why all question have the same data.
You should have had create an array of question contains it own question's content like
<template>
<div v-for="(question, index) in questions" :key="index">
<form #submit.prevent="legitStore(question)" method="post">
...
<div class="col-md-12">
<p>
<strong>{{question.id}}</strong>
</p>
</div>
...
<select class="mb-1 field" v-model="question.answer" name="answer" id="answer">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
...
</form>
</div>
</template>
<script>
class QuestionForm {
// pass default value if you want
name = ''
answer = ''
description = ''
duration = ''
id = ''
constructor(form) {
Object.assign(this, form)
}
}
function initQuestions(num) {
// can generate a Set object cause question are unique
return Array.from({ length: num }, (v, i) => i).map(_j => new QuestionForm())
}
export default {
data() {
return {
questions: initQuestions(5), // pass number of question you want to generate
}
}
}
</script>
component.html
<div class="form-group">
<label>Enter mobile</label>
<input type="text" class="form-control" formControlName="mobile" ><br>
</div>
<div *ngIf="userGroup.controls.mobile.invalid && (userGroup.controls.mobile.dirty || userGroup.controls.mobile.touched)">
<div *ngIf="userGroup.controls.mobile.errors.required">
Mobile number cannot be blank
</div>
<div *ngIf="userGroup.controls.mobile.errors.pattern">
Mobile number should be 10 digits only
</div>
</div>
component.ts
userGroup:FormGroup;
ngOnInit() {
this.userGroup = this.fb.group({
mobile:['',Validators.required,Validators.pattern(/^[0-9]{10}$/)]
});
}
For the blank it is working perfectly but for pattern it is not showing any error
Try like this:
mobile:['',[Validators.required,Validators.pattern(/^[0-9]{10}$/)]]
});
component.ts
userGroup:FormGroup;
//simple getter to easily access control from template
get mobile() { return this.userGroup.get('mobile'); }
ngOnInit() {
this.userGroup = this.fb.group({
//also validators should passed in one array as a second argument to this literal
mobile:['', [Validators.required, Validators.pattern(/^[0-9]{10}$/)]]
});
component.html
<div class="form-group">
<label>Enter mobile</label>
<input type="text" class="form-control" formControlName="mobile"><br>
</div>
<div *ngIf="mobile.invalid && (mobile.dirty || mobile.touched)">
<div *ngIf="mobile.errors.required">
Mobile number cannot be blank
</div>
<div *ngIf="mobile.errors.pattern">
Mobile number should be 10 digits only
</div>
</div>
For more than 1 validator, you should use it inside an array as second parameter. otherwise it will return an error on console and wont work.
I'm new to MVC and AJAX so this is probably a simple mistake I am making but using the code below, I am getting the following error trying to getElementById("txtCount").value:
<div class="row">
<div class="col-sm-4">
<div class="panel panel-primary">
<div class="panel-heading">
<h5 style="font-weight:bold;">Parameters</h5>
</div>
<div class="panel-body" id="parameters">
<form class="form-horizontal" id="frmParameters">
<div class="form-group">
<label for="txtCount" class="col-sm-4 col-form-label">Repeat</label>
<input type="number" min="1" max="100" step="1" id="txtCount" value="#Model.Count" class="input-sm col-sm-7" />
</div>
#if (Model.Grammar.SupportsMaxLength)
{
<div class="form-group">
<label for="txtMaxLength" class="col-sm-4 col-form-label">Max Length</label>
<input type="number" min="1" max="100" step="1" id="txtMaxLength" value="#Model.MaxLength" class="input-sm col-sm-7" />
</div>
}
<button name="btnGenerate" class="btn btn-primary pull-right" onclick="Generate();">Generate</button>
</form>
</div>
</div>
</div>
</div>
<script>
function Generate() {
var data = { count: document.getElementById("txtCount").value, maxLength: document.getElementById("txtMaxLength").value };
}
</script>
If I change:
var data = { count: document.getElementById("txtCount").value, maxLength: document.getElementById("txtMaxLength").value };
to:
var data = { count: document.getElementById("txtCount").value};
I don't get the error anymore.
Your code looks fine. I think you are getting the error when your code tries to execute this line
document.getElementById("txtMaxLength").value
Because in your view you are rendering this element when some if condition returns true. So it is possible that your view does not have this input element at all and you are trying to read that! (Check the view source of the page and search for input with txtMaxLength id.
The best solution is to check it exists before trying to read the value.
var data = {
id: "#Model.Id",
count: document.getElementById("txtCount").value,
maxLength: null // or whatever default value you want
};
if (document.getElementById("txtMaxLength")) {
data2.maxLength = document.getElementById("txtMaxLength").value;
}
Or if you are using jQuery library, it is easy
var data = {
id: "#Model.Id",
count: $("#txtCount").val(),
maxLength:$("#txtMaxLength").val()
};
So, I have this form, made using AngularJS here, which basically lets me create a purchase object to send to a server, i.e, it lets me select a store where I bought some items, set a "date of purchase" (just a text field for now), and add those items to the object I'm gonna send.
After the submit button it is shown how the model I'm going to send will look like, showing the id of the store, the "datetime", and an array of items.
My question is: Is there a way of doing this form using angular-formly only?
The question arises because I've been reading formly's docs and I haven't figured out how to make it create such a dynamic model as this form does, i.e., with a variable-length array of items of the purchase, or if it is at all possible.
Thanks in advance for any clue you can give me to answer this question :)
The code for the form is as follows:
(function(){
var app = angular.module('test', []);
})();
The html page:
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular.js"></script>
<script src="inab.js"></script>
<script src="PurchaseCtrl.js"></script>
</head>
<body ng-app="test">
<div ng-controller="PurchaseCtrl" class="col-md-4">
<h2>Purchase</h2>
<div class="panel panel-default">
<div class="panel-heading">Title</div>
<div class="panel-body">
<div class="form-group">
<label>Store</label>
<select class="form-control" ng-model="model.store">
<option ng-repeat="store in stores" value="{{store.id}}">{{store.name}}</option>
</select>
</div>
<div class="form-group">
<label>date-time</label>
<input class="form-control" type="text" ng-model="model.datetime"/>
</div>
<div ng-repeat="item in items">
<div class="form-group">
<div class="col-sm-2">
<label>{{item.label}}</label>
</div>
<div class="col-sm-8">
<input class="form-control" type="text" ng-model="item.nome" />
</div>
<div class="col-sm-2">
<button type="submit" class="btn btn-alert submit-button col-md-2" ng-click="removeItem()">remove item</button>
</div>
</div>
</div>
<button ng-click="addItem()">Add item</button>
</div>
</div>
<button type="submit" class="btn btn-primary submit-button" ng-click="onSubmit()">Submit</button>
<pre>{{model | json}}</pre>
</div>
</body>
</html>
The controller:
(function(){
angular.module('test').controller('PurchaseCtrl', ['$scope', function(scope){
scope.stores = [{id: 1, name:'Store 1'}, {id: 2, name: 'Store 2'}];
scope.items = [];
scope.datetime = '';
scope.store = '';
var i = 0;
scope.model = {
store: scope.store,
datetime: scope.datetime,
items: scope.items
};
scope.addItem = function(){
scope.items.push({label: 'algo' + (i++), nome:''});
}
scope.removeItem = function(){
scope.items.splice(scope.items.length - 1);
}
scope.onSubmit = function(){
console.log(scope.model);
}
}]);
})();
As #Satej commented, it was with repeated sections. Thanks :)
I have a pretty big form that's being validated on the client side by Angular. I am trying to figure out how to reset the state of the form and its inputs just clicking on a Reset button.
I have tried $setPristine() on the form but it didn't really work, meaning that it doesn't clear the ng-* classes to reset the form to its original state with no validation performed.
Here's a short version of my form:
<form id="create" name="create" ng-submit="submitCreateForm()" class="form-horizontal" novalidate>
<div class="form-group">
<label for="name" class="col-md-3 control-label">Name</label>
<div class="col-md-9">
<input required type="text" ng-model="project.name" name="name" class="form-control">
<div ng-show="create.$submitted || create.name.$touched">
<span class="help-block" ng-show="create.name.$error.required">Name is required</span>
</div>
</div>
</div>
<div class="form-group">
<label for="lastName" class="col-md-3 control-label">Last name</label>
<div class="col-md-9">
<input required type="text" ng-model="project.lastName" name="lastName" class="form-control">
<div ng-show="create.$submitted || create.lastName.$touched">
<span class="help-block" ng-show="create.lastName.$error.required">Last name is required</span>
</div>
</div>
</div>
<button type="button" class="btn btn-default" ng-click="resetProject()">Reset</button>
</form>
And my reset function:
$scope.resetProject = function() {
$scope.project = {
state: "finished",
topic: "Home automation"
};
$("#create input[type='email']").val('');
$("#create input[type='date']").val('');
$scope.selectedState = $scope.project.state;
// $scope.create.$setPristine(); // doesn't work
}
Also if you could help me clear the input values of the email and date fields without using jQuery would be great. Because setting the $scope.project to what's defined above doesn't reset the fields for some reason.
Try to clear via ng-model
$scope.resetProject = function() {
$scope.project = {
state: "finished",
topic: "Home automation"
};
$("#create input[type='email']").val('');
$("#create input[type='date']").val('');
$scope.selectedState = $scope.project.state;
$scope.project = {
name: "",
lastName: ""
};
}
As mentioned in the comments, you can use $setUntouched();
https://docs.angularjs.org/api/ng/type/form.FormController#$setUntouched
This should set the form back to it's new state.
So in this case $scope.create.$setUntouched(); should do the trick
Ref all that jquery. You should never interact with the DOM via controllers. That's what the directives are for
If you want to reset a given property then do something like:
$scope.resetProject = function() {
$scope.project = {
state: "finished",
topic: "Home automation"
};
$scope.project.lastName = '';
$scope.project.date= '';
}