Using $("form").serialize() in E-Commerce Shopping Cart - asp.net-mvc-2

I am working on an e-commerce application shopping cart. I have a pricing page which display a list of products and their prices for multiple categories. User can choose to add multiple products to shopping cart (active shopping cart is being shown on right hand side of page). I am trying to use Ajax/jQuery for adding items to my cart. I have a form wrapped around each product which contains multiple hidden fields I would like to pass to my function and to the controller. You can see all these in the code below:
<% foreach (var _category in Model) { %>
<% foreach (var _product in _category.Products)
{ %>
<tr>
<td align="left" valign="top"><% = _product.Description %> (<% = _product.Code %>)</td>
<td valign="top" align="center">$<% = _product.TotalPrice %></td>
<td align="left">
<form id="frmProduct_<%=_product.Code%>">
<input type="button" onclick="JavaScript:addProductToBasket(this.form);" value="+ Add to cart" />
<input type="text" id="hProductCode" value="<% = _product.Code %>" />
<input type="text" id="Text1" value="<% = _product.TotalPrice %>" />
<!--Other hidden fields for passing data -->
</form>
</td>
</tr>
<% } %>
<% } %>
Since I have multiple forms on page, I am having hard time accessing a particular form inside my javascript function. What is a best way to handle this scenario?
<script type="text/javascript">
function addProductToBasket(_form) {
alert('Hi');
var str = $('#_form').serialize();
alert(str);
}
</script>
I am using ASP.NET MVC 2.0 and cannot move to MVC 3.0 at the moment.

Try to make your forms working without using javascript first. Then start thinking about all the ajax and jQuery stuff.
Remove the id attribute from the forms and add an action attribute (or use the MVC method: Using Html.BeginForm), add a class attribute to the form tag.
Remove the Totalprice field, you should always calculate this server-side, the only fields you need to submit are the product code (and a quantity).
Remove the javascript button and replace it with a classic submit button.
When you want to ajaxify the form, try something like this:
$(function () {
$(".addproductform").submit(function () { // turn all forms with the addproductform class into an ajax version
$.post($(this).attr("action"), $(this).serialize(), function (data) {
// data contains the confirmation or failure that the product was added to your cart, update the cart html on this page
});
return false; // form already submitted using ajax, don't submit it again the regular way
});
});

Related

Angular 2 Dynamic Nested Form

Basically I want to create a dynamic form with nested objects like the picture below:
Pay offs are in an array on the model
We should be able to add/remove pay offs as needed.
The form should sync underlying form controls and model
The number of pay offs is arbitrary and should be loaded into the form from the model
There are no working examples that I could find as how to do this in Angular 2, although this was really easy to do in Angular 1.
Below is my original question, I've since updated it for clarification (see above):
First I just wanted to point out that I'm aware that a new version of Angular 2 rc.2 has just been released a few days ago. So the code for creating a dynamic, nested form may have changed some but there's not enough documentation to figure this out.
In the latest version(s) of Angular 2 (I'm currently using rc.1 but planning to update to rc.2) I need to create a form like this (pseudo-code of view):
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
<input type="text" ngControl="name">
<div *ngFor="let expense for expenses; let i = index;" control-group="expenses">
<input type="text" ngControl="expense.amount" [(ngModel)]="myModel.expenses[i].amount">
<input type="checkbox" ngControl="expense.final" [(ngModel)]="myModel.expenses[i].final">
</div>
<a class="button" (click)="addExpenseControl()">Add</a>
<a class="button" (click)="deleteExpenseControl()">Delete</a>
</form>
So the pseudo-code above won't work but to be honest because of lack of documentation I can't figure out how to wire something like this up. There's a few tutorials about nested ControlGroup but this won't fit the case here since we need to be able to dynamically add and remove control groups, and also I need to be able to sync them with a model.
I found this plunkr here provided by Angular team which allows adding of Controls to a form--but this is not adding/removing a ControlGroup, rather it's using ControlArray and I'm not sure if that applies here?
I'm very familiar with using the newer model-based Angular 2 forms however I'm grasping for resources in order to properly nest them (dynamically!), and tie this nested data into the main form model. How would I refer to nested controls in the view? Is the pseudo-code above even close? I'd post code from my controller but honestly I wouldn't know where to start when it comes to the nested expenses (ControlGroup ??) above...
I had to figure this out on my own because it seems that forms are still changing in Angular 2 and I've not seen any other examples similar to this (although it seems like a very common use-case).
Here is a plunkr of working example using Angular2 RC3.
I am using updated Angular 2 form code from this document.
app.component.ts (contains the form):
import { Component } from '#angular/core';
import {REACTIVE_FORM_DIRECTIVES, FormControl, FormGroup, FormArray} from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: 'app/app.html',
directives: [REACTIVE_FORM_DIRECTIVES],
providers: []
})
export class AppComponent {
form: FormGroup;
myModel:any;
constructor() {
// initializing a model for the form to keep in sync with.
// usually you'd grab this from a backend API
this.myModel = {
name: "Joanna Jedrzejczyk",
payOffs: [
{amount: 111.11, date: "Jan 1, 2016", final: false},
{amount: 222.22, date: "Jan 2, 2016", final: true}
]
}
// initialize form with empty FormArray for payOffs
this.form = new FormGroup({
name: new FormControl(''),
payOffs: new FormArray([])
});
// now we manually use the model and push a FormGroup into the form's FormArray for each PayOff
this.myModel.payOffs.forEach(
(po) =>
this.form.controls.payOffs.push(this.createPayOffFormGroup(po))
);
}
createPayOffFormGroup(payOffObj) {
console.log("payOffObj", payOffObj);
return new FormGroup({
amount: new FormControl(payOffObj.amount),
date: new FormControl(payOffObj.date),
final: new FormControl(payOffObj.final)
});
}
addPayOff(event) {
event.preventDefault(); // ensure this button doesn't try to submit the form
var emptyPayOff = {amount: null, date: null, final: false};
// add pay off to both the model and to form controls because I don't think Angular has any way to do this automagically yet
this.myModel.payOffs.push(emptyPayOff);
this.form.controls.payOffs.push(this.createPayOffFormGroup(emptyPayOff));
console.log("Added New Pay Off", this.form.controls.payOffs)
}
deletePayOff(index:number) {
// delete payoff from both the model and the FormArray
this.myModel.payOffs.splice(index, 1);
this.form.controls.payOffs.removeAt(index);
}
}
Notice above that I manually push new FormGroup objects into the form.controls.payOffs array, which is a FormArray object.
app.html (contains form html):
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<label>Name</label>
<input type="text" formControlName="name" [(ngModel)]="myModel.name" placeholder="Name">
<p>Pay Offs</p>
<table class="simple-table">
<tr>
<th>Amount</th>
<th>Date</th>
<th>Final?</th>
<th></th>
</tr>
<tbody>
<tr *ngFor="let po of form.find('payOffs').controls; let i = index">
<td>
<input type="text" size=10 [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount">
</td>
<td>
<input type="text" [formControl]="po.controls.date" [(ngModel)]="myModel.payOffs[i].date">
</td>
<td>
<input type="checkbox" [formControl]="po.controls.final" [(ngModel)]="myModel.payOffs[i].final">
</td>
<td>
<button (click)="deletePayOff(i)" style="color: white; background: rgba(255, 0, 0, .5)">x</button>
</td>
</tr>
</tbody>
<tr>
<td colspan="4" style="text-align: center; padding: .5em;">
<button (click)="addPayOff($event)" style="color: white; background: rgba(0, 150, 0, 1)">Add Pay Off</button>
</td>
</tr>
</table>
</form>
In the html form I link the form to the model on the inputs with statements like so:
... [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount" ...

Trigger validation of all fields in Angular Form submit

I'm using this method: http://plnkr.co/edit/A6gvyoXbBd2kfToPmiiA?p=preview to only validate fields on blur. This works fine, but I would also like to validate them (and thus show the errors for those fields if any) when the user clicks the 'submit' button (not a real submit but a data-ng-click call to a function)
Is there some way to trigger validation on all the fields again when clicking that button?
What worked for me was using the $setSubmitted function, which first shows up in the angular docs in version 1.3.20.
In the click event where I wanted to trigger the validation, I did the following:
vm.triggerSubmit = function() {
vm.homeForm.$setSubmitted();
...
}
That was all it took for me. According to the docs it "Sets the form to its submitted state." It's mentioned here.
I know, it's a tad bit too late to answer, but all you need to do is, force all forms dirty. Take a look at the following snippet:
angular.forEach($scope.myForm.$error.required, function(field) {
field.$setDirty();
});
and then you can check if your form is valid using:
if($scope.myForm.$valid) {
//Do something
}
and finally, I guess, you would want to change your route if everything looks good:
$location.path('/somePath');
Edit: form won't register itself on the scope until submit event is trigger. Just use ng-submit directive to call a function, and wrap the above in that function, and it should work.
In case someone comes back to this later... None of the above worked for me. So I dug down into the guts of angular form validation and found the function they call to execute validators on a given field. This property is conveniently called $validate.
If you have a named form myForm, you can programmatically call myForm.my_field.$validate() to execute field validation. For example:
<div ng-form name="myForm">
<input required name="my_field" type="text" ng-blur="myForm.my_field.$validate()">
</div>
Note that calling $validate has implications for your model. From the angular docs for ngModelCtrl.$validate:
Runs each of the registered validators (first synchronous validators and then asynchronous validators). If the validity changes to invalid, the model will be set to undefined, unless ngModelOptions.allowInvalid is true. If the validity changes to valid, it will set the model to the last available valid $modelValue, i.e. either the last parsed value or the last value set from the scope.
So if you're planning on doing something with the invalid model value (like popping a message telling them so), then you need to make sure allowInvalid is set to true for your model.
You can use Angular-Validator to do what you want. It's stupid simple to use.
It will:
Only validate the fields on $dirty or on submit
Prevent the form from being submitted if it is invalid
Show custom error message after the field is $dirty or the form is submitted
See the demo
Example
<form angular-validator
angular-validator-submit="myFunction(myBeautifulForm)"
name="myBeautifulForm">
<!-- form fields here -->
<button type="submit">Submit</button>
</form>
If the field does not pass the validator then the user will not be able to submit the form.
Check out angular-validator use cases and examples for more information.
Disclaimer: I am the author of Angular-Validator
Well, the angular way would be to let it handle validation, - since it does at every model change - and only show the result to the user, when you want.
In this case you decide when to show the errors, you just have to set a flag:
http://plnkr.co/edit/0NNCpQKhbLTYMZaxMQ9l?p=preview
As far as I know there is a issue filed to angular to let us have more advanced form control. Since it is not solved i would use this instead of reinventing all the existing validation methods.
edit: But if you insist on your way, here is your modified fiddle with validation before submit. http://plnkr.co/edit/Xfr7X6JXPhY9lFL3hnOw?p=preview
The controller broadcast an event when the button is clicked, and the directive does the validation magic.
One approach is to force all attributes to be dirty. You can do that in each controller, but it gets very messy. It would be better to have a general solution.
The easiest way I could think of was to use a directive
it will handle the form submit attribute
it iterates through all form fields and marks pristine fields dirty
it checks if the form is valid before calling the submit function
Here is the directive
myModule.directive('submit', function() {
return {
restrict: 'A',
link: function(scope, formElement, attrs) {
var form;
form = scope[attrs.name];
return formElement.bind('submit', function() {
angular.forEach(form, function(field, name) {
if (typeof name === 'string' && !name.match('^[\$]')) {
if (field.$pristine) {
return field.$setViewValue(field.$value);
}
}
});
if (form.$valid) {
return scope.$apply(attrs.submit);
}
});
}
};
});
And update your form html, for example:
<form ng-submit='justDoIt()'>
becomes:
<form name='myForm' novalidate submit='justDoIt()'>
See a full example here: http://plunker.co/edit/QVbisEK2WEbORTAWL7Gu?p=preview
Here is my global function for showing the form error messages.
function show_validation_erros(form_error_object) {
angular.forEach(form_error_object, function (objArrayFields, errorName) {
angular.forEach(objArrayFields, function (objArrayField, key) {
objArrayField.$setDirty();
});
});
};
And in my any controllers,
if ($scope.form_add_sale.$invalid) {
$scope.global.show_validation_erros($scope.form_add_sale.$error);
}
Based on Thilak's answer I was able to come up with this solution...
Since my form fields only show validation messages if a field is invalid, and has been touched by the user I was able to use this code triggered by a button to show my invalid fields:
// Show/trigger any validation errors for this step
angular.forEach(vm.rfiForm.stepTwo.$error, function(error) {
angular.forEach(error, function(field) {
field.$setTouched();
});
});
// Prevent user from going to next step if current step is invalid
if (!vm.rfiForm.stepTwo.$valid) {
isValid = false;
}
<!-- form field -->
<div class="form-group" ng-class="{ 'has-error': rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched && rfi.rfiForm.stepTwo.Parent_Suffix__c.$invalid }">
<!-- field label -->
<label class="control-label">Suffix</label>
<!-- end field label -->
<!-- field input -->
<select name="Parent_Suffix__c" class="form-control"
ng-options="item.value as item.label for item in rfi.contact.Parent_Suffixes"
ng-model="rfi.contact.Parent_Suffix__c" />
<!-- end field input -->
<!-- field help -->
<span class="help-block" ng-messages="rfi.rfiForm.stepTwo.Parent_Suffix__c.$error" ng-show="rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched">
<span ng-message="required">this field is required</span>
</span>
<!-- end field help -->
</div>
<!-- end form field -->
Note: I know this is a hack, but it was useful for Angular 1.2 and earlier that didn't provide a simple mechanism.
The validation kicks in on the change event, so some things like changing the values programmatically won't trigger it. But triggering the change event will trigger the validation. For example, with jQuery:
$('#formField1, #formField2').trigger('change');
I like the this approach in handling validation on button click.
There is no need to invoke anything from controller,
it's all handled with a directive.
on github
You can try this:
// The controller
$scope.submitForm = function(form){
//Force the field validation
angular.forEach(form, function(obj){
if(angular.isObject(obj) && angular.isDefined(obj.$setDirty))
{
obj.$setDirty();
}
})
if (form.$valid){
$scope.myResource.$save(function(data){
//....
});
}
}
<!-- FORM -->
<form name="myForm" role="form" novalidate="novalidate">
<!-- FORM GROUP to field 1 -->
<div class="form-group" ng-class="{ 'has-error' : myForm.field1.$invalid && myForm.field1.$dirty }">
<label for="field1">My field 1</label>
<span class="nullable">
<select name="field1" ng-model="myresource.field1" ng-options="list.id as list.name for list in listofall"
class="form-control input-sm" required>
<option value="">Select One</option>
</select>
</span>
<div ng-if="myForm.field1.$dirty" ng-messages="myForm.field1.$error" ng-messages-include="mymessages"></div>
</div>
<!-- FORM GROUP to field 2 -->
<div class="form-group" ng-class="{ 'has-error' : myForm.field2.$invalid && myForm.field2.$dirty }">
<label class="control-label labelsmall" for="field2">field2</label>
<input name="field2" min="1" placeholder="" ng-model="myresource.field2" type="number"
class="form-control input-sm" required>
<div ng-if="myForm.field2.$dirty" ng-messages="myForm.field2.$error" ng-messages-include="mymessages"></div>
</div>
</form>
<!-- ... -->
<button type="submit" ng-click="submitForm(myForm)">Send</button>
I done something following to make it work.
<form name="form" name="plantRegistrationForm">
<div ng-class="{ 'has-error': (form.$submitted || form.headerName.$touched) && form.headerName.$invalid }">
<div class="col-md-3">
<div class="label-color">HEADER NAME
<span class="red"><strong>*</strong></span></div>
</div>
<div class="col-md-9">
<input type="text" name="headerName" id="headerName"
ng-model="header.headerName"
maxlength="100"
class="form-control" required>
<div ng-show="form.$submitted || form.headerName.$touched">
<span ng-show="form.headerName.$invalid"
class="label-color validation-message">Header Name is required</span>
</div>
</div>
</div>
<button ng-click="addHeader(form, header)"
type="button"
class="btn btn-default pull-right">Add Header
</button>
</form>
In your controller you can do;
addHeader(form, header){
let self = this;
form.$submitted = true;
...
}
You need some css as well;
.label-color {
color: $gray-color;
}
.has-error {
.label-color {
color: rgb(221, 25, 29);
}
.select2-choice.ui-select-match.select2-default {
border-color: #e84e40;
}
}
.validation-message {
font-size: 0.875em;
}
.max-width {
width: 100%;
min-width: 100%;
}
To validate all fields of my form when I want, I do a validation on each field of $$controls like this :
angular.forEach($scope.myform.$$controls, function (field) {
field.$validate();
});

Submitting a Form using Jquery

Okay so I am converting some code to jQuery and currently the js is just changing the focus to a button with the target id using whenever you press enter or double click in a <select> tag. document.getElementById.focus() and then document.getElementById.click() and returning true to submit this form. Just looking for some example on how to do same thing using jQuery instead. I understand that there is a .keypress() and a .dblclick() function in jQuery and thats what I think I should be using but passing the values of the input box or the select values are a little difficult since there are multiples of each in the form. FYI this is a search page that sends SQL to an oracle database.
Update-
$(document).ready(function(){
$('form').submit(function(){
$(this).keypress()
if(event.which ==13){
}
});
});
This is what i have so far not sure if i am on the right track or not.
so here is an example of how the form is.
<form>
<tr>
<td nowrap="nowrap"><b> Search by Number </b></td>
<td style="vertical-align:top"><input type="text" name="revisor_number" value=revisor_number>" size="55" maxlength="100" /><br/><span style="font-size:75%">commas between numbers (10,15,20), dash for range(10-20)</span><br/></td>
<td> <input type="submit" name="submit_number" id="submit_number" value="GO"/></td>
</tr>
<tr>
<td style="vertical-align:top" nowrap="nowrap"><b> Search by Type </b></td>
<td>
<select name="subtype[]" size="3" multiple="multiple" onkeypress="keyPress(event, 'submit_subtype');" ondblclick="keyPress(event, 'submit_subtype');">
<option value="">>--- All ---</option>
<td style="vertical-align:top"> <input type="submit" name="submit_subtype" id="submit_subtype" value="GO"/></td>
You need to move it outside of your submit function, replace it with:
$('input, textarea').keypress(function(e) {
if(e.which == 13) {
$(this).blur();
$('#submit').focus().click();
}
});
Assuming '#submit' is the ID of your button.
I don't know if i understand what you want,
but submitting a form with a button in jQuery is something like :
$('button').on('click', function(){
$('yourForm').submit();
});

Why is FilteringSelect in declarative dijit form causing invalid submit?

Help me understand this.
Isn't dijit.form.FilteringSelect (extended from ValidationTextBox) supposed to have property required = false by default?
Why is it that simply including a FilteringSelect in a declarative form like so below automatically results in dijit.form.Form.isValid() == false?
Even manually setting the filteringselect's required prop to false results in an invalid form submit. I feel like there's something I'm missing here.
I'm Using dojo toolkit version 1.6.1.
<!-- form.html -->
<form id="form" dojoType="dijit.form.Form">
<table>
<tr>
<td id="friend">
<select name="friend" id="friend-input" dojotype="dijit.form.FilteringSelect"></select>
</td>
</tr>
<tr>
<td>
<input type="submit" id="submit-input" value="Submit" label="Submit" dojotype="dijit.form.Button">
</td>
</tr>
</table>
</form>
/* form.js */
dojo.require("dijit.form.Button");
dojo.require("dijit.form.FilteringSelect");
dojo.require("dijit.form.Form");
dojo.ready(function() {
var form = dijit.byId("form");
var friendInput = dijit.byId("friend-input");
friendInput.required = false;
dojo.connect(form, "onSubmit", function(event) {
event.preventDefault();
if (form.isValid()) {
alert("Ready to submit data: " + dojo.toJson(form.get("value")));
} else {
alert("Form is not valid.");
}
});
});
Like Frode mentioned, we need to set the required to false.
But, lots of fields might be used. Not a good idea to set 'required' for each, in the dojo.ready section.
<select name="friend" id="friend-input"
dojotype="dijit.form.FilteringSelect" required="false"></select>
The better way is to mention it as attribute in the html itself. Let me give an example why it is better.
If the field is included in a tab, and if the tab is refreshed on certain actions, the html will be arsed again. So, in that scenario, the required will be true again for that field. So, therefore, provide it in the html declaration of the widget itself to avoid these scenarios.

Symfony dynamic forms

I started with a form, which is made by hand because of it's complexity (it's a javascript modified form, with sortable parts, etc). The problem is that now I need to do the validation, and it's a total mess to do it from scratch in the action using the sfValidator* classes.
So, I am thinking to do it using sfForm so that my form validation and error handling can be done more easier and so I can reuse this form for the Edit and Create pages.
The form is something like this:
<form>
<input name="form[year]"/>
<textarea name="form[description]"></textarea>
<div class="sortable">
<div class="item">
<input name="form[items][0][name]"/>
<input name="form[items][0][age]"/>
</div>
<div class="item">
<input name="form[items][1][name]"/>
<input name="form[items][1][age]"/>
</div>
</div>
</form>
The thing is that the sortable part of the form can be expanded from 2 to N elements on the client side. So that it has variable items quantity which can be reordered.
How can I approach this problem?
Any ideas are welcome,
thank you. :)
I'm doing something similar using embedded forms for the repeating fields.
In your form class you could do something like:
$form_data = $this->getObject();
if (isset($form_data['items']) && is_array($form_data['items']))
{
$items_form = new BaseForm();
foreach ($form_data['items'] as $count => $values)
{
$form = new BaseForm();
$form->widgetSchema['name'] = new sfWidgetFormInputText();
$form->widgetSchema['age'] = new sfWidgetFormInputText();
$items_form->embedForm($count, $form);
}
$this->embedForm('items', $items_form);
$this->validatorSchema['items'] = new sfValidatorPass(array('required' => false));
}