Why aren't all my data validations not validating as designed? - angularjs

I am developing this application in Angular 2 that has a form that you can populate and if the form is not populated, it will prompt you with what needs to be populated. It works for Customer Name, but then for the rest, for example, "a list of tools is required" does not go away once populated. Here is the code below under app/proposal/proposal-new.component.html:
<h1>Create a Proposal</h1>
<div>
<form #proposalForm="ngForm">
<div class="form-group">
<label for="customer">Customer Name</label>
<input type="text" id="customer" placeholder="ABC Supply" required name="customer" #customer='ngModel' [(ngModel)]="proposal.customer">
<div [hidden]="customer.valid || customer.pristine">
Customer name is required
</div>
</div>
<div class="form-group">
<label for="portfolio_url">Portfolio URL</label>
<input type="text" id="portfolio_url" placeholder="ABC Supply" required name="portfolio_url" #portfolio_url='ngModel' [(ngModel)]="proposal.portfolio_url">
<div [hidden]="portfolio_url.valid || portfolio_url.pristine">
A Portfolio URL is required
</div>
</div>
<div class="form-group">
<label for="portfolio_url">Tools that you'll use on the project</label>
<input type="text" id="portfolio_url" placeholder="Ruby on Rails, Angular, etc" required name="tools" #portfolio_url='ngModel' [(ngModel)]="proposal.tools ">
<div [hidden]="tools.valid || tools.pristine">
A list of tools is required
</div>
</div>
<div class="form-group">
<label for="estimated_hours">Estimated hours</label>
<input type="number" id="estimated_hours" required name="estimated_hours" #portfolio_url='ngModel' [(ngModel)]="proposal.estimated_hours ">
<div [hidden]="estimated_hours.valid || estimated_hours.pristine">
You need to enter your estimated hours for the project
</div>
</div>
<div class="form-group">
<label for="hourly_rate">Hourly rate</label>
<input type="number" id="hourly_rate" required name="hourly_rate" #portfolio_url='ngModel' [(ngModel)]="proposal.hourly_rate ">
<div [hidden]="hourly_rate.valid || hourly_rate.pristine">
You need to enter your hourly rate for the project
</div>
</div>
<div class="form-group">
<label for="weeks_to_complete">Weeks to Complete</label>
<input type="number" id="weeks_to_complete" required name="weeks_to_complete" #weeks_to_complete='ngModel' [(ngModel)]="proposal.weeks_to_complete ">
<div [hidden]="weeks_to_complete.valid || weeks_to_complete.pristine">
You need to enter the weeks you estimate to complete the project
</div>
</div>
<div class="form-group">
<label for="weeks_to_complete">Client Email <em>(Optional)</em></label>
<input type="email" id="weeks_to_complete" name="weeks_to_complete" #client_email='ngModel' [(ngModel)]="proposal.client_email">
</div>
</form>
<div>
<p>Hi {{proposal.customer}},</p>
<p>It was a pleasure getting to meet with you and review your project requirements, I believe it is a great fit for the types of applications that I build out. Please feel free to check out some of my past projects here.</p>
<p>To successfully build out the application I will be utilizing {{proposal.tools}}, and a number of other tools to ensure that the project follows industry wide best practices.</p>
<p>Ensuring that you are fully informed is one of my top priorities, so in addition to incorporating everything we discussed, I will also be creating a project management dashboard and sending you a project update message daily so that you can have a transparent view of the development as it takes place.</p>
<p>To accomplish the project and meet the requirements that we discussed, I am estimating that it will take {{proposal.estimated_hours}} hours in development time to complete. The project should take {{proposal.weeks_to_complete}} weeks to complete and with my hourly rate of {{proposal.hourly_rate}}/hour, the estimated total will be {{proposal.hourly_rate * proposal.estimated_hours}}.</p>
<p>The next step from here is to set up a meeting to finalize the project and sign contracts.</p>
<p>I am excited to working with you and build out this project.</p>
</div>
Here is the app/proposal/proposal.ts:
export class Proposal {
constructor(
public id?: number,
public customer?: string,
public portfolio_url: string = 'http://',
public tools?: string,
public estimated_hours?: number,
public hourly_rate?: number,
public weeks_to_complete?: number,
public client_email?: string,
) {}
}
If there is any other files I should be looking at and posting, please let me know. I am stumped here.

The issue you are seeing is that the local variable portfolio_url is being defined multiple times instead of a new local variable for each input. If you update the local variable definitions, which look like this: #[var_name]='ngModel', to have a unique name for each the validation should work as expected.
Update your HTML to the following:
<h1>Create a Proposal</h1>
<div>
<form #proposalForm="ngForm">
<div class="form-group">
<label for="customer">Customer Name</label>
<input type="text"
id="customer"
placeholder="ABC Supply"
required name="customer"
#customer='ngModel'
[(ngModel)]="proposal.customer">
<div [hidden]="customer.valid || customer.pristine">
Customer name is required
</div>
</div>
<div class="form-group">
<label for="portfolio_url">Portfolio URL</label>
<input type="text"
id="portfolio_url"
placeholder="ABC Supply"
required
name="portfolio_url"
#portfolio_url='ngModel'
[(ngModel)]="proposal.portfolio_url">
<div [hidden]="portfolio_url.valid || portfolio_url.pristine">
A Portfolio URL is required
</div>
</div>
<div class="form-group">
<label for="portfolio_url">Tools that you'll use on the project</label>
<input type="text"
id="tools"
placeholder="Ruby on Rails, Angular, etc"
required
name="tools"
#tools='ngModel'
[(ngModel)]="proposal.tools ">
<div [hidden]="tools.valid || tools.pristine">
A list of tools is required
</div>
</div>
<div class="form-group">
<label for="estimated_hours">Estimated hours</label>
<input type="number"
id="estimated_hours"
required
name="estimated_hours"
#estimated_hours='ngModel'
[(ngModel)]="proposal.estimated_hours ">
<div [hidden]="estimated_hours.valid || estimated_hours.pristine">
You need to enter your estimated hours for the project
</div>
</div>
<div class="form-group">
<label for="hourly_rate">Hourly rate</label>
<input type="number"
id="hourly_rate"
required
name="hourly_rate"
#hourly_rate='ngModel'
[(ngModel)]="proposal.hourly_rate ">
<div [hidden]="hourly_rate.valid || hourly_rate.pristine">
You need to enter your hourly rate for the project
</div>
</div>
<div class="form-group">
<label for="weeks_to_complete">Weeks to Complete</label>
<input type="number"
id="weeks_to_complete"
required
name="weeks_to_complete"
#weeks_to_complete='ngModel'
[(ngModel)]="proposal.weeks_to_complete ">
<div [hidden]="weeks_to_complete.valid || weeks_to_complete.pristine">
You need to enter the weeks you estimate to complete the project
</div>
</div>
<div class="form-group">
<label for="weeks_to_complete">Client Email <em>(Optional)</em></label>
<input type="email"
id="weeks_to_complete"
name="weeks_to_complete"
#client_email='ngModel'
[(ngModel)]="proposal.client_email">
</div>
</form>
<div>
<p>Hi {{proposal.customer}},</p>
<p>It was a pleasure getting to meet with you and review your project requirements, I believe it is a great fit for the types of applications that I build out. Please feel free to check out some of my past projects here.</p>
<p>To successfully build out the application I will be utilizing {{proposal.tools}}, and a number of other tools to ensure that the project follows industry wide best practices.</p>
<p>Ensuring that you are fully informed is one of my top priorities, so in addition to incorporating everything we discussed, I will also be creating a project management dashboard and sending you a project update message daily so that you can have a transparent view of the development as it takes place.</p>
<p>To accomplish the project and meet the requirements that we discussed, I am estimating that it will take {{proposal.estimated_hours}} hours in development time to complete. The project should take {{proposal.weeks_to_complete}} weeks to complete and with my hourly rate of {{proposal.hourly_rate}}/hour, the estimated total will be {{proposal.hourly_rate * proposal.estimated_hours}}.</p>
<p>The next step from here is to set up a meeting to finalize the project and sign contracts.</p>
<p>I am excited to working with you and build out this project.</p>
</div>
Working Plunkr

You shouldn't need to do this:
#portfolio_url='ngModel' [(ngModel)]="proposal.portfolio_url"
In the Angular 2 docs they say:
The hash (#) prefix to "phone" means that we're defining a phone variable
So in your case you are defining portfolio_url multiple times in your code. You do not need to use the hash prefix because the portfolio_url is already defined in your proposal object.
The following should work (read more about the template binding syntax):
<input type="number" id="hourly_rate" required name="hourly_rate" [(value)]="proposal.hourly_rate">
Or you can do this:
<input type="number" id="hourly_rate" required name="hourly_rate" #proposal.hourly_rate>
The angular 2 docs also have another example of user input: https://angular.io/docs/ts/latest/guide/user-input.html

Related

Why does Cypress not recognise the testid of a react-datepicker element?

Target
To make Cypress recognise and manipulate a DatePicker element.
Problem
I cannot make Cypress recognise the data-testid of a DatePicker element, and so have no way of testing it E2E.
Error
Timed out retrying: Expected to find element: [data-testid="edit_dob"], but never found it.
Attempts
I have tried placing the DatePicker element inside input and div elements to target instead, but they fail as expected for other reasons.
I have also looked through the docs for both DatePicker and Cypress for mention of the other, and for similar questions here.
Code
The code below works as expected, it's just Cypress that seems to be incompatible with the DatePicker input.
function displayEditDetails() {
let listOfForms = [];
for (const detail in details) {
listOfForms.push(
<FormGroup controlId={detail} key={`key_edit_${detail}`}>
<FormLabel>{detail.charAt(0).toUpperCase() + detail.slice(1).replace('_', ' ')}</FormLabel>
<FormControl
data-testid={`edit_${detail}`}
type={detail}
value={details[detail] || ''}
onChange={handleDetailsChange}
/>
</FormGroup>
)
}
listOfForms.push(
<DatePicker
data-testid={'edit_dob'}
selected={dob}
onChange={setDob}
maxDate={new Date()}
/>
);
return(
listOfForms
)
}
Test
it('Displays the edit fields', () => {
cy.get('[data-testid="accountButton"]')
.click();
cy.get('[data-testid="editUserDetailsButton"]')
.click();
cy.get('[data-testid="user_email"]')
.should('not.be.visible');
cy.get('[data-testid="edit_full_name"]')
.should('be.visible');
cy.get('[data-testid="edit_dob"]')
.should('be.visible');
cy.get('[data-testid="edit_email"]')
.should('be.visible')
.should('have.value', 'test#editdetails.com');
})
Once I can target it, I plan to test clearing the current input and typing a new date in. Any help targeting the picker and warnings of further potential pitfalls are appreciated.
Edit: Here's the generated HTML of the form.
<form>
<div class="form-group">
<label class="form-label" for="email">Email</label>
<input data-testid="edit_email" type="email" id="email" class="form-control" value="admin#example.com" style="background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHklEQVQ4EaVTO26DQBD1ohQWaS2lg9JybZ+AK7hNwx2oIoVf4UPQ0Lj1FdKktevIpel8AKNUkDcWMxpgSaIEaTVv3sx7uztiTdu2s/98DywOw3Dued4Who/M2aIx5lZV1aEsy0+qiwHELyi+Ytl0PQ69SxAxkWIA4RMRTdNsKE59juMcuZd6xIAFeZ6fGCdJ8kY4y7KAuTRNGd7jyEBXsdOPE3a0QGPsniOnnYMO67LgSQN9T41F2QGrQRRFCwyzoIF2qyBuKKbcOgPXdVeY9rMWgNsjf9ccYesJhk3f5dYT1HX9gR0LLQR30TnjkUEcx2uIuS4RnI+aj6sJR0AM8AaumPaM/rRehyWhXqbFAA9kh3/8/NvHxAYGAsZ/il8IalkCLBfNVAAAAABJRU5ErkJggg=="); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%; cursor: auto;">
</div>
<div class="form-group">
<label class="form-label" for="full_name">Full name</label>
<input data-testid="edit_full_name" type="full_name" id="full_name" class="form-control" value="Jareth">
</div>
<div class="form-group">
<label class="form-label" for="address">Address</label>
<input data-testid="edit_address" type="address" id="address" class="form-control" value="">
</div>
<div class="form-group">
<label class="form-label" for="postcode">Postcode</label>
<input data-testid="edit_postcode" type="postcode" id="postcode" class="form-control" value="">
</div>
<div data-testid="edit_dob">
<div class="react-datepicker-wrapper">
<div class="react-datepicker__input-container">
<input type="text" class="" value="04/03/1999">
</div>
</div>
</div>
<button data-testid="confirmChangesButton" type="submit" class="LoaderButton btn btn-primary btn-block">Confirm Changes</button></form>
The problem is visible when looking at the HTML generated by running the app.
DatePicker HTML
<div data-testid="edit_dob">
<div class="react-datepicker-wrapper">
<div class="react-datepicker__input-container">
<input type="text" class="" value="04/03/1999">
</div>
</div>
</div>
The DatePicker is rendered as an input element inside a few divs, and the test id doesn't target the input.
Solution
The Selector Playground API for Cypress suggests cy.get('.react-datepicker__input-container > input'), the class of the parent div of the input. With this I can target the DatePicker, clear it, and type new input.
Notes For Next Time
Examine the HTML
Read the Docs more thoroughly
Reference the concepts used in the cy.pickDateRange command in the Cypress Real World App. It is a payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows.
In the code for the command, you will find it is necessary to examine the HTML produced and traverse it appropriately for your needs.

React 'fetch' response successful, but setState not working

I'm new to React, working through a ".Net core CRUD with React" tutorial and trying to tweak it along the way to suit my own needs.
The page I'm dealing with here is an Add/Edit entry page. It works fine for rendering a default form with default values but doesn't render anything if the values are collected from a fetch call.
The important details are below:
interface AddPortfolioProjectDataState {
title: string;
projectData: PortfolioProject;
loading: boolean;
}
The page is told to render as follows:
public render() {
let contents = this.state.loading
? <p><em>Loading Project...</em></p>
: this.renderCreateForm(this.state.projectData.type, this.state.projectData.tech);
return (
<div>
<h1>{this.state.title}</h1>
<h3>Project</h3>
<hr />
{contents}
</div>
)
}
If I want to add a new item, therefore using a default PortfolioProject object with default values, it works fine. However, if I want to edit an old entry, I have to grab it from the server, like so:
fetch('api/StuartAitkenWebsite/GetPortfolioProject/' + projID)
.then(response => response.json() as Promise<PortfolioProject>)
.then(data => {
this.setState({ title: "Edit", loading: false, projectData: data });
});
In the debug console on Firefox, I can see the whole server process runs smoothly:
GET http://localhost:62669/api/StuartAitkenWebsite/GetPortfolioProject/2
Response payload: {"id":2,"name":"Particles Sim","projectDate":"2017-01-01T00:00:00","projectDurationWeeks":1,"type":"Desktop App","tech":"C++, SFML","views":0,"creationDate":"2018-10-22T00:00:00","modifiedDate":"2018-10-22T00:00:00","status":1}`
It gives a JSON output of the payload too, which I can't easily copy-paste here so I'll give a screenshot:
There are no server error responses, no React errors, nothing.
But that's as far as it gets.
The page remains showing 'loading', even though the data is there and ready and wants to be displayed.
From this, I can gather that the final step of the fetch call is not succeeding, because this.setState({ title: "Edit", loading: false, projectData: data }); is clearly not having any effect on the page data.
I have other fetch calls which look exactly the same but work fine. I can't see what I'm missing here.
The one and the only difference I notice is this:
When I use this component to create a fresh 'Add Project' form, the state is set like so:
this.state = {
title: "Create",
loading: false,
projectData: new PortfolioProject,
};
But when I do it from the API, it's set like so:
this.setState({
title: "Edit",
loading: false,
projectData: data
});
The successful version uses this.state, and the unsuccessful version uses this.setState
I don't know what this can mean though. As I said, no errors are being thrown, I'm sticking to the tutorial format, and it works fine in other parts of the project.
Thanks.
UPDATE
I've put a log in at the point where renderCreateForm() is called. It seems setState is actually working. Therefore, the problem must be in renderCreateForm() so I'll post that code below. Sorry it's sort of large.
private renderCreateForm(projectTypes: string, projectTech: string) {
console.log(this.state.loading); // "false"
console.log(this.state.projectData); //"Object { id:1, name: "Muon Detector".. etc
//so the render is getting the data
return (
<form onSubmit={this.handleSave}>
<div className="form-group row" >
<input type="hidden" name="Id" value={this.state.projectData.id} />
</div>
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Name">Name</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Name" defaultValue={this.state.projectData.name} required />
</div>
</div>
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="ProjectDate">Project Date</label>
<div className="col-md-4">
<input className="form-control" type="date" name="ProjectDate" defaultValue={this.state.projectData.creationDate.toDateString()} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="ProjectDurationWeeks">Project Duration (weeks)</label>
<div className="col-md-4">
<input className="form-control" type="text" name="ProjectDurationWeeks" defaultValue={this.state.projectData.projectDurationWeeks.toString()} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Type">Project Type</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Type" defaultValue={this.state.projectData.type} required />
</div>
</div >
<div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Tech">Project Tech</label>
<div className="col-md-4">
<input className="form-control" type="text" name="Tech" defaultValue={this.state.projectData.tech} required />
</div>
</div >
<div className="form-group row" >
<input type="hidden" name="Views" value={this.state.projectData.views} />
</div>
<div className="form-group row" >
<input type="hidden" name="CreationDate" value={this.state.projectData.creationDate.toDateString()} />
</div>
<div className="form-group row" >
<input type="hidden" name="ModifiedDate" value={this.state.projectData.modifiedDate.toDateString()} />
</div>
<div className="form-group row" >
<input type="hidden" name="Status" value={this.state.projectData.status} />
</div>
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form>
)
}
UPDATE 2: Added some screenshots showing how things appear so far.
How the main data table page looks:
If I click 'Add New', it works:
(the 'Save' option works there too. Data posts to the server and will list on the main portfolio page)
Clicking Edit for any of the entries does not work, it gets this far:
The 'Loading Project...' text comes from the render() call for this page, as is shown in the code posted at the top of this post.
The page is supposed to look exactly like the 'Create' page (2nd screenshot), but with the title being 'Edit', and with input values populated from the given data.
The solution was absurd, but may certainly help others...
The renderCreateForm() method (as shown in Update 1 of the post) was not working because of the .toDateString() method I was using in a few of the inputs.
I changed it to .toString() and now everything works.
For example, with an input like this:
<input className="form-control" type="date" name="ProjectDate" defaultValue={projectData.creationDate.toDateString()} required />
I changed it to this:
<input className="form-control" type="date" name="ProjectDate" defaultValue={projectData.creationDate.toString()} required />
Note the defaultValue property of the input.
Repeat for all cases of .ToDateString(), and it now works,
Amazing that this didn't bring up an error anywhere. I thought Typescript and all these various frameworks were supposed to get around the issue of Javascript silently failing like that. This has been my longest and most time-wasting 'silent fail error' ever, by a very long margin.

Interview form with angularjs (repeat and model)

I want to make interview questions form with ng-repeat, where when i click submit, it will send all the data, so i need different ng-model for that.
Here is my code
<form role="form" name="interviewForm">
<div class="pr_setportfolio_content interview_div" ng-show="setinterview == true">
<div class="prterms_name_input" ng-repeat="interview in interview">
<span class="prterms_input_text">
{{interview.question}}
</span>
<textarea class="prterms_inputtextarea_name" ng-model="interview_answer" name="interview_answer" placeholder="Enter answer" msd-elastic auto-resize></textarea>
</div>
</div>
</form>
So, anyone have any idea how to make it?
Thank you
Use same model (for example interview.answer instead of interview_answer) for holding answers and then you can get answer for each question in same model. Just iterate on interview list and each index will hold one question and its answer
<form role="form" name="interviewForm">
<div class="pr_setportfolio_content interview_div" ng-show="setinterview == true">
<div class="prterms_name_input" ng-repeat="interview in interview">
<span class="prterms_input_text">
{{interview.question}}
</span>
<textarea class="prterms_inputtextarea_name" ng-model="interview.answer" name="interview_answer" placeholder="Enter answer" msd-elastic auto-resize></textarea>
</div>
</div>
Try to use
ng-repeat="intview in interview"
<textarea class="prterms_inputtextarea_name" data-ng-model="intview.interview_answer" name="interview_answer" placeholder="Enter answer" msd-elastic auto-resize></textarea>
After submitting this form, just collect the answer interview.interview_answer is not null or undefined.

Basic Save form data the Angular way

I am trying to figure out the "correct" way to get and set form data using Angular. (By "correct" I mean one that supports scalability to larger apps, where controllers aren't overly simple, with shortcut references, as they are in most demos.) When I look though the examples, the seem so basic (such as no controllerAs specifier) that I can't figure out what's pointing at what.
Anyway, I'm expecting that there is an object somewhere in my viewmodel, accessible by the controller, that contains the data in its named fields, suitable for PUTing to the api - and doesn't contain all the cruft that is associated with the actual form and field objects:
To-wit: this is what settingsVm.settingsForm contains:
$dirty:true
$error:{}
$invalid:false
$name:"settingsVm.settingsForm"
$pristine:false
$submitted:true
$valid:true
Email:"asdsa#asdsad.com"
Id:{$validators: {}, $asyncValidators: {}, $parsers: [], $formatters: [], $viewChangeListeners: [],…}
$asyncValidators:{}
$dirty:false
$error:{}
$formatters:[]
$invalid:false
$name:"Id"
$options:null
$parsers:[]
$pristine:true
$touched:false
$untouched:true
$valid:true
$validators:{}
$viewChangeListeners:[]
StreetDirection:""
StreetName:"asdasdsadsada"
StreetNumber:"34"
Suite:""
Here is my form, with just a couple of fields:
<input type="hidden" name="Id" ng-model="settingsVm.settings.Id" />
<div class="form-group row">
<label for="Email" class="col-sm-1 col-form-label text-right"> Email:</label>
<div class="col-sm-5">
<input type="text" class="form-control form-group-med" maxlength="50" name="Email" ng-model="settingsVm.settingsForm.Email" required />
<div class="error-message" ng-show="settingsVm.settingsForm.Email.$invalid && settingsVm.settingsForm.Email.$touched || settingsVm.settingsForm.submitted">
<span ng-show="settingsVm.settingsForm.Email.$error.required"> Email is required.</span >
</div>
</div>
</div>
<div class="form-group row">
<label for="StreetName" class="col-sm-1 col-form-label text-right"> Name:</label>
<div class="col-sm-5">
<input type="text" class="form-control form-group-med" maxlength="50" name="Email" ng-model="settingsVm.settingsForm.StreetName" required />
<div class="error-message" ng-show="settingsVm.settingsForm.StreetName.$invalid && settingsVm.settingsForm.StreetName.$touched || listingVm.submitted">
<span ng-show="settingsVm.settingsForm.StreetName.$error.required">Street Name is required.</span >
</div>
</div>
</div>
</form>
Am I using the ng-model correctly? And am I using the $error correctly?
Should my model be a distinct object from my form? Maybe my model should point at settingsVm.settingsData instead of settingsVm.settingsForm.
Here is my submit:
vm.submit = function (isValid) {
vm.submitted = true;
if (isValid) {
settingsService.putSettings(vm.settingsForm);
}
};
Not sure if this will help you after 3 months, but I figured it out, please correct me if I am wrong somewhere.
Yes your statement is right, you actually need to separate form validation and input fields.
I assume when you are using field validation angular actually uses input field names not the ng-Model values. So this works for me
var myapp = angular.module("taskApp", [])
.controller("taskCtrl", function($scope, $http) {
$scope.form = {};
$scope.makeTask = function(data) {
console.dir(data);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-App="taskApp" ng-controller="taskCtrl">
<form name="TaskForm" novalidate ">
<div class="form-group ">
<label>Name</label>
<input name="Name " ng-required="true " class="form-control " ng-model="form.Name " />
<span ng-show="TaskForm.Name.$invalid && TaskForm.Name.$dirty ">The name is required</span>
</div>
<button ng-Click="makeTask(form) " type="submit ">Add</button>
</form>
<div id="output ">OUTPUT</div>

Validating nested form in angular

Having this ordinary (name attribute is requred by server) form with angular and can't figured out how to make validations work. What should i put into ng-show="TODO"
http://jsfiddle.net/Xk3VB/7/
<div ng-app>
<form ng-init="variants = [{duration:10, price:100}, {duration:30, price:200}]">
<div ng-repeat="variant in variants" ng-form="variant_form">
<div>
<label>Duration:</label>
<input name="variants[{{$index}}][duration]" ng-model="variant.duration" required />
<span ng-show="TODO">Duration required</span>
</div>
<div>
<label>Price:</label>
<input name="variants[{{$index}}][price]" ng-model="variant.price" />
<span ng-show="TODO">Price required</span>
</div>
</div>
</form>
</div>
ps: this is just piece of form, which is more complicated
Thanks
AngularJS relies on input names to expose validation errors.
Unfortunately, as of today it is not possible (without using a custom directive) to dynamically generate a name of an input. Indeed, checking input docs we can see that the name attribute accepts a string only.
Long story short you should rely on ng-form to validate dynamically created inputs. Something like :
<div ng-repeat="variant in variants" >
<ng-form name="innerForm">
<div>
<label>Duration:</label>
<input name="duration" ng-model="variant.duration" required />
<span ng-show="innerForm.duration.$error.required">Duration required</span>
</div>
<div>
<label>Price:</label>
<input name="price" ng-model="variant.price" required/>
<span ng-show="innerForm.price.$error.required">Price required</span>
</div>
</ng-form>
Working fiddle here
UPDATE : Base on your serverside requirement why not do something like that :
<input type="hidden" name="variants[{{$index}}][duration]" ng-model="variant.duration"/>
<input name="duration" ng-model="variant.duration" required />
The hidden input will be the one read by the server while the other one will be used to do the client side validation (later discarded by server). It s kind of an hack but should work.
PS : Be sure that your form is valid before actually submitting it. Can be done with ng-submit

Resources