Building a form from json - asp.net-mvc

I'm fetching an array of viewmodels from my controller using jquery+json. I then build a form where each row in a table represents one viewmodel.
My question is: How should I name each form element so that I can get it to my controller action like this:
public ActionResult Update(MyViewModel[] models)
{
}
Edit: I'm using jquery-tmpl to generate the form, and I'm also trying to figure out how to get an index variable in it (if that's needed for the form generation).

I managed to get it working.
My jquery template:
<script id="wagonTemplate" type="text/x-jquery-tmpl">
<tr>
<td>
<input type="checkbox" value="true" class="wagoncheck" name="wagons[${$item.getIndex()}].IsSelected" />
</td>
<td>
<input type="text" name="wagons[${$item.getIndex()}].WagonId" value="${WagonId}" style="width:120px" />
</td>
<td>
<input type="text" name="wagons[${$item.getIndex()}].WagonNumber" value="${WagonNumber}" style="width:20px" />
</td>
</script>
Method that loads the template:
function loadWagons(trainId, partId) {
$.getJSON('/train/wagons/' + escape(trainId) + '?partNo=' + partId, function (data) {
$wagons = $('#wagons tbody');
$wagons.empty();
// the function used in the template to get an index.
var tmplOptions = {
getIndex: function getIndex() {
return $.inArray(this.data, data);
}
};
$("#wagonTemplate").tmpl(data, tmplOptions).appendTo($wagons);
});
}
In other words:
To get a YourModel[] items argument in your controller action you need to name the items as items[0].MyProperty' where0` should correspond to the index in the array.
To get an index in a jquery template, just use pass a method in the options to the template function. I'm using a slightly modified version of the answer found here. Passing the item as done in that answer is not necessary as this points on the current item.

Related

Getting input value to action method. Mvc Core & AJAX

I'm new to MVC Core and i'm having some struggles getting this right.
I've got a table filled with some basic values from my products, and what i want is to send a quantity value and an id of the product to my action method. The problem i'm having is that i'm able to send my product.ID to the action method, but i can't seem to get my input value. I tried using a button instead of my element and when i used that i managed to get the input but not the product.ID.
#model List<Product>
<div id="productList">
<form>
<table>
#foreach (var product in Model)
{
<tr>
<td>#product.Name</td>
<td>#product.Price</td>
<td><input type="text" name="quantity" /></td>
<td>
<a
asp-action="AddProductToCart"
asp-route-id="#product.ID"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="#cartinfo">Add to basket</a>
</td>
</tr>
}
</table>
</form>
</div>
<div id="cartinfo">
</div>
My action methods parameters looks like this:
public IActionResult AddProductToCart(int id, int quantity)
I'm sure i'm missing some basic knowledge about how forms work so i'd really appreciate getting some help here. I've been trying to google this but i'm struggling with that as well. Thanks a lot
You can use javascript instead.
#model List<Product>
<div id="productList">
<form>
<table>
#foreach (var product in Model)
{
<tr>
<td style="visibility:hidden" class="pID">#product.ID</td>
<td>#product.Name</td>
<td>#product.Price</td>
<td><input type="text" name="quantity" class="qty"/></td>
<td>
<button class="btnAdd" >Add to basket</button>
</td>
</tr>
}
</table>
</form>
</div>
<div id="cartinfo">
</div>
java script
<script type="text/javascript">
$(document).ready(function () {
$('.btnAdd').click(function () {
var PID= $(this).closest("tr").find(".pID").text();
var Pqty= $(this).closest("tr").find(".qty").text();
AddtoCart(PID, Pqty);
});
});
function AddtoCart(pid,qty) {
$.ajax({
url: "#Url.Action("AddProductToCart", "Your Controller")",
type: 'GET',
data: { id: pid, quantity: qty},
datatype: 'json',
success: function (data) {
$('#cartinfo').html(data);
}
});
}
</script>
Hope this will help you!
Oh man, now i know what developers mean when they say that their code from 1 year ago is trash. Not sure if i should feel embarrassed or proud, haha.
My solution now would've probably been to post using JS. Also i wouldn't have placed my "asp-route-id="#product.ID" on an element, i could've just put it as a hidden input and posted it. Oh and that data-ajax-mode stuff confused me more than it helped, remove that for sure.
Note to self: Keep improving. :-)
You could try to put data-ajax attribute in the <form> ,and make the following changes in your view and the parameter in the action
<div id="productList">
<table>
#foreach (var product in Model)
{
<tr>
<form data-ajax="true"
data-ajax-url="/Your controllerName/AddProductToCart"
data-ajax-method="Post"
data-ajax-mode="replace"
data-ajax-update="#cartinfo">
<td>#product.Name</td>
<td>#product.Price</td>
<td>
<input type="text" asp-for="#product.quantity"/>
<input asp-for="#product.Id" hidden />
</td>
<td>
<input type="submit" value="Add to basket"/>
</td>
</form>
</tr>
}
</table>
Change the parameters to Model object , note that the parameter name must be consistent with the name of the data passed from the client side
[HttpPost]
public IActionResult AddProductToCart( Product product)
{
//the stuff you want
}

Read <input> value inside view in MVC

Hi i want to create table that has as many rows as user can set in input field.
how to do that instead of 5 in for loop i have value specified in input tag ?
#Using (Html.BeginForm("AddNewProces", "Admin", FormMethod.Post))
#<input type="number" value="5" min="1" name="rowsNumber" />
#<table>
#For i As Integer = 0 To 5
#<tr>
<td>Row #i</td>
</tr>
Next i
</table>
End Using
EDIT:
Ok i decided to change some things but now it still doesent work ;/ this is what i try now:
<script type="text/javascript">
$('.rowsNumber').on('input', function () {
alert("Value Changed");
});
</script>
and here is my htlm.textboxfor
#Html.TextBoxFor(Function(m) m.NumberOfRows, New With {.class = "rowsNumber"})
You would need to do this client-side, not server-side. The server-side code can't read the value of the input before the page has even been rendered.
So your view markup would simplify:
#Using (Html.BeginForm("AddNewProces", "Admin", FormMethod.Post))
#<input type="number" value="5" min="1" name="rowsNumber" />
#<table id="myTable">
</table>
End Using
Then you'd attach a JavaScript handler to the input to modify the table. It might have a structure that's something like this:
$('input[name="rowsNumber"]').on('input', function () {
var existingRowCount = $('#myTable tr').length;
var newRowCount = parseInt($(this).val());
if (newRowCount == existingRowCount) {
return;
}
if (newRowCount > existingRowCount) {
// append new rows to the table
}
if (newRowCount < existingRowCount) {
// remove trailing rows from the table
}
});

add item to observable array in viewmodel from another partial view (MVC)

I am new to knockoutJS. I am working on an MVC application where I want to implement knockoutJS but the scenario is bit different.
I have a page where I am showing a list. I have 3 links on the page and on click of them I am adding partial views to page accordingly. What I want to do is that whenever I add values/data to partial views, the list which is on page should be updated with knockout. In other words I want to add value to observable array when I save data from partial view.
Please let me know if this is possible or I should keep it in jquery only.
Here is the code:
Main view:
<input type="button" value="Add Partial View" onclick="LoadPartial();" />
<div id="dvContent"></div>
<h4>People</h4>
<ul data-bind="foreach: people">
<li>
Name at position <span data-bind="text: $index"> </span>:
<span data-bind="text: name"> </span>
Remove
</li>
</ul>
<button data-bind="click: addPerson">Add</button>
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script>
function LoadPartial() {
$.ajax({
url: "/home/index",
dataType:"html",
type: "GET",
success: function (data) {
$("#dvContent").html(data);
}
});
}
</script>
<script>
function AppViewModel() {
var self = this;
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function () {
self.people.push({ name: "New at " + new Date() });
};
self.removePerson = function () {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
</script>
Partial View:
<table>
<tr>
<td>Add new Row</td>
<td><input type="button" value="Add" data-bind="click: addPerson"/></td>
</tr>
</table>
Thanks,
JsHunjan
It is easy to accomplish with Knockout. You need to show some code that you have tried though if you want to get some help. I will post a general answer but it isn't going to fix your use case exactly, just basically -
Create an object to hold your new item, you can do this either in the parent or the child view model, but if you do it in the child you need to pass it back to the parent.
Once you hit a save button or add or whatever in the child view model just do a .push() into the observableArray that you created ex... - myObservableArray.push(newItem());
Knockout will recognize all of the changes taking place and perform the actions you want automatically.
Hope this helps.

ASP.NET MVC 3 EF Code First - Master Details CRUD

I am using ASP.NET MVC 3 EF Code First with Razor + SQLserver and Want to implement Master Details scenario (like Order, Orderlines) with CRUD operations. I have come across some online examples like http://hasibulhaque.com/index.php/2011/master-detail-crud-operations-ef-asp-net-mvc-3/ but they heavily depends on JQuery or other complex implementations. Can somebody suggest me some step by step approach with a clean code?
there are good tutorials at the asp.net site.
and i recommend you switch to mvc4 learning.
here is a link:
http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4
If you want scaffolding do it for you, unfortunately it's not possible and you can't do that simply. Besides, you must use jquery and ajax to implement what you want.
I think the best and simplest way for you is that you have a view for creating Form and at the bottom of it put a fieldset to assign FormFields to it.
For the fieldset, you should have two partial views: One for create and another for edit. The partial view for creating should be something like this:
#model myPrj.Models.Form_FormFieldInfo
#{
var index = Guid.NewGuid().ToString();
string ln = (string)ViewBag.ListName;
string hn = ln + ".Index";
}
<tr>
<td>
<input type="hidden" name="#hn" value="#index" />
#Html.LabelFor(model => model.FormFieldID)
</td>
<td>
#Html.DropDownList(ln + "[" + index + "].FormFieldID",
new SelectList(new myPrj.Models.DbContext().FormFields, "ID", "FieldName"))
</td>
<td>
<input type="button" onclick="$(this).parent().parent().remove();"
value="Remove" />
</td>
</tr>
By calling this partial view in the create place view ajaxly, you can render some elements for each tag. Each line of elements contains a label, a DropDownList containing tags, and a remove button to simply remove the created elements.
In the create place view, you have a bare table which will contain those elements you create through the partial view:
<fieldset>
<legend>Form and FormFields</legend>
#Html.ValidationMessageFor(model => model.FormFields)</label>
<table id="tblFields"></table>
<input type="button" id="btnAddTag" value="Add new Field"/>
<img id="imgSpinnerl" src="~/Images/indicator-blue.gif" style="display:none;" />
</fieldset>
and you have the following script to create a line of elements for each tag:
$(document).ready(function () {
$("#btnAddField").click(function () {
$.ajax({
url: "/Controller/GetFormFieldRow/FormFields",
type: 'GET', dataType: 'json',
success: function (data, textStatus, jqXHR) {
$("#tblFields").append(jqXHR.responseText);
},
error: function (jqXHR, textStatus, errorThrown) {
$("#tblFields").append(jqXHR.responseText);
},
beforeSend: function () { $("#imgSpinnerl").show(); },
complete: function () { $("#imgSpinnerl").hide(); }
});
});
});
The action method GetFormFieldRow is like the following:
public PartialViewResult GetFormFieldRow(string id = "")
{
ViewBag.ListName = id;
return PartialView("_FormFieldPartial");
}
and your done for the create... The whole solution for your question has many codes for views, partial views, controllers, ajax calls and model binding. I tried to just show you the way because I really can't to post all of them in this answer.
Here is the full info and how-to.
Hope that this answer be useful and lead the way for you.

Using Knockout bindings in MVC ActionLink

I am attempting to utilise KnockoutJS and MVC 4 in order to display a table with ActionLink definitions in the first column of the table. Displaying the data itself is extremely straight-forward and I'm not having any problem there. The problem I have is in the generation of the ActionLink's.
I have taken a look at Use MVC helpers inside jquery.tmpl templates, but the solution there does not utilise knockout templates and inserting the Url into the model object is not feasible (the app domain model objects used to create the view model will be used extensively through out the application).
The table definition:
<table>
<tbody data-bind="template: { name: 'dmuTableDetail', foreach: tables() }"></tbody>
</table>
(tables is an observable array, hence the parens).
The knockout template definition:
<script id="dmuTableDetail" type="text/html">
<tr>
<td>#Html.ActionLink("Details", "Details", "DMUTableCategory", new { #Id = ??? } )</td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>​
The View Model definition:
var PageViewModel = function () {
self = this;
self.tables = ko.observableArray([]);
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
}
}
$(document).ready(function () {
vm = new PageViewModel();
self.readItems('');
ko.applyBindings(vm);
});
(the actual code performs an Ajax call to retrieve the data, but the code above also demonstrates the issue).
Regardless of what I replace the ??? with, I am unable to get the value of the TableId field to be inserted into the href.
Any help would be greatly appreciated.
Thankyou.
Thankyou Eric, you got me thinking about an anchor element and binding the href attribute.
It seems the answer is a little easier than expected (it usually is!).
The table definition: (same as original question)
<table>
<tbody data-bind="template: { name: 'dmuTableDetail', foreach: tables() }"></tbody>
</table>
The knockout template definition: (change to the binding of the href attribute).
<script id="dmuTableDetail" type="text/html">
<tr>
<td><a data-bind="attr: { 'href': '#Url.Action("Details", new RouteValueDictionary() { { "Controller", "DMUTableCategory" } } )/' + TableId }">Details</a></td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>?
The View Model definition: (same as original question)
var PageViewModel = function () {
self = this;
self.tables = ko.observableArray([]);
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
}
}
$(document).ready(function () {
vm = new PageViewModel();
self.readItems('');
ko.applyBindings(vm);
});
You dont actually need to RootValueDictionary but I've included it so people can see how to change the controller the request is sent to.
Knockout binds completely on the client side, which is after MVC has rendered the HTML for your page and sent it back to the original browser.
If you want your Knockout template to be able to use a URL that is generated on the server, then you'll have to employ some clever strategy similar to the following:
CSHTML:
#{
// create a dummy URL that you can use as a template
string hrefFormat = Url.Action("Details", "DMUTableCategory", new { id = "{ID}" });
}
<script type="javascript">
// a global string (but you can put it where ever you need to)
var _hrefFormat = #Html.Raw(hrefFormat)
<script>
JS:
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
// loop through the 'tables' and add a new 'href' property to each for binding
ko.utils.arrayForEach(self.tables(), function(table){
table.href = _hrefFormat.replace("{ID}", table.TableId);
});
}
Your KO Tmpl where you bind the 'href' property of each table object to the a tag's href attribute:
<script id="dmuTableDetail" type="text/html">
<tr>
<td><a data-bind="attr: { 'href': href }">Details</a></td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>​

Resources