Let say I have a model DailyTasks and a model Task. The initial view is strongly typed with DailyTasks model. It shows a list of existing tasks for the day. Users can add more task to the list/table by clicking the add button. When the add button is clicked, I render a partial view which is tied to a Task model.
I want to be able to save any changes the user made to the existing tasks and as well as newly added tasks.
I'm not sure sure what is the best way to do this. I have been playing with model binding and as well as creating a json object of the model and then pass it to the controller upon Save. So far I was only able to pass back the existing tasks to the Save controller, none of the newly added tasks show up.
Model:
public class DailyTasks
{
public int ID { get; set; }
public List<Task> TaskList{ get; set; }
}
public class Task
{
public int Id { get; set; }
public string MyTask { get; set; }
}
Main View:
#model Example.Models.DailyTasks
#using (Ajax.BeginForm("Save", "DailyTasks", new AjaxOptions { HttpMethod = "Post" }))
{
<input type="button" value="Add New Task" id="addBtn" />
<input type="submit" value="Save" id="saveBtn"/>
<table class="table">
<tr>
<th>Column Header Name Goes Here</th>
<th>Column Header Name Goes Here</th>
</tr>
#for (var i = 0; i < Model.TaskList.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(m => Model.TaskList[i].ID)
#Html.HiddenFor(m => Model.TaskList[i].ID)
</td>
<td>
#Html.DisplayFor(m => Model.TaskList[i].MyTask)
#Html.HiddenFor(m => Model.TaskList[i].MyTask)
</td>
</tr>
}
</table>
}
<script type="text/javascript">
$(document).ready(function () {
$("#addBtn").on("click", function () {
$.get('#Url.Action("AddTask")', function (data) {
$("table tbody").append(data);
});
});
});
</script>
Add New Task AcitionResult for Partial View:
public ActionResult AddTask()
{
Task model = new Task();
return PartialView("_AddTask", model);
}
Partial View (_AddTask):
#model Example.Models.Task
<tr>
<td>
#Html.DisplayFor(m => Model.ID)
#Html.HiddenFor(m => Model.ID)
</td>
<td>
#Html.DisplayFor(m => Model.MyTask)
#Html.HiddenFor(m => Model.MyTask)
</td>
</tr>
I found exactly what I needed here: http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ Now I can use modelbinding method instead of the traditional ways (FormCollection and Request).
I hope it helps someone in the future. Also, thanks to Tobias for the link. It is definitely discusses core problem that i presented but unfortunately the solution found there didn't work in my case.
Cheers!
Related
I have a separate TR and inside, a TD from the rest of my table. I have some data in my model that contains a list of strings, and also a list of IDs (not sure if I need the list of IDS for this) and I would like to display on the lower Tr's td a specific part of the list, based off of the selection of a SelectListItem in the table row's td above it.. i.e. If a user select's a list item of X, I want the TD below to display "X's help description" (which like I mentioned earlier, is being stored inside a list of strings in my model)
I am not sure if I should be doing this in Razor, Javascript, or something else. Can anyone give me some tips? Below is some code.
View:
<div class="row">
<div class="col-md-12" style="overflow-y:scroll">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Terminal</th>
<th>Command</th>
<th>Command Value</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.DropDownListFor(o => o.TerminalsDDL, Model.TerminalsDDL, new { id = "ddlTerminalID", #class = "form-control" })</td>
<td>#Html.DropDownListFor(o => o.TerminalCommandLookupsDDL, Model.TerminalCommandLookupsDDL, new {id = "ddlCommandValue", #class = "form-control" })</td>
<td>#Html.TextBoxFor(o => o.UserEnteredTerminalCommands, new { Class = "form-control", Id = "cmdValueValue"})</td>
<td> <input id="btnSaveTerminalCommand" type="button" value="Insert" class="btn btn-primary" /> </td>
</tr>
<tr>
<td colspan="4" id="helpDescript">#Html.DisplayFor(model => model.HelpDescription)</td>
</tr>
</tbody>
</table>
</div>
</div>
VM:
public TerminalCommandVM()
{
//Terminals Drop Down List
TerminalsDDL = new List<SelectListItem>();
//Terminal Commands Drop Down List
TerminalCommandLookupsDDL = new List<SelectListItem>();
//Terminal Command Values list
TerminalCommandValues = new List<SelectListItem>();
}
public TerminalCommand TerminalCommand { get; set; }
public List<TerminalCommand> TerminalCommands { get; set; }
[Display(Name = "Terminal ID")]
public List<SelectListItem> TerminalsDDL { get; set; }
[Display(Name = "Command")]
public List<SelectListItem> TerminalCommandLookupsDDL { get; set; }
public List<SelectListItem> TerminalCommandValues { get; set; }
public string UserEnteredTerminalCommands { get; set; }
public List<string> HelpDescription { get; set; }
public List<int> HelpDescriptionID { get; set; }
}
The DisplayFor I want populated is the one with the ID = "helpDescript", and the select list item that should dictate which help descript is displayed has the ID = "ddlCommandValue".
As of now, helpDescript is displaying the entire list (obviously).
If anyone needs any other code or more information, please let me know.
Try the following. In the dropdown change event call the action to display the value and in the success function display the value in label
$("#ddlCommandValue").change(function () {
var obj = {
valueToPass: $(this).val()
};
$.ajax({
url: '/Home/GetValueToDisplayInlabel',
contentType: 'application/json; charset=utf-8',
type: 'POST',
data: JSON.stringify(obj),
cache: false,
success: function (result) {
$("#helpDescript").html(result);
},
error: function () {
alert("Error");
}
});
I have a strongly typed IEnumerable as the Model for one of my views.
I'm iterating over that model in the view to display the content of each model in a HTML table where each row represents a model.
When the user clicks on one of the rows, I'd like the model of that row to render in a jquery dialog where it displays the model content in a pop up.
Can someone explain how to pass these C# objects to the jQuery dialog function, or any other alternative method to achieve this would be appreciated.
Thanks
View Model
public class DTO
{
public int Id { get; set; }
[RegularExpression(#"\d+(\.\d{0})?")]
public decimal Amount { get; set; }
public string Type { get; set; }
}
View
#model IEnumerable<DTO>
<div id="expenses-list"">
#if (Model.Count() > 0)
{
<table class="table borderless">
<tbody>
<tr>
<th class="input-group-horiz">Expense</th>
</tr>
#foreach (var item in Model)
{
#Html.Partial("Item", item);
}
</tbody>
</table>
}
</div>
Partial View
#model DTO
<div id="#Model.Id">
<tr>
<td>#Model.Type</td>
<td>#Model.Amount</td>
<td><a onclick="DisplayDialog(#Model)"><img src="~/content/images/153-check.png" /></a></td>
<td><img src="~/Content/Images/17-bin.png"></td>
</tr>
</div>
JQuery Dialog
<script>
function DisplayDialog(model) {
$('#dialog').dialog({
autoOpen: true,
modal: true,
title: 'Add Expense',
width: 800,
open: function () {
}
});
}
I have a HTML table as below in my View:
<table id="tblCurrentYear">
<tr>
<td>Leave Type</td>
<td>Leave Taken</td>
<td>Leave Balance</td>
<td>Leave Total</td>
</tr>
#foreach (var item in Model.LeaveDetailsList)
{
<tr>
<td>#Html.TextBoxFor(m => item.LeaveType, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveTaken, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveBalance, new { width = "100" })</td>
<td>#Html.TextBoxFor(m => item.LeaveTotal, new { width = "100" })</td>
</tr>
}
</table>
I want to iterate through all the html table rows and insert the values in ADO.NET DataTable.
Simple speaking, converting HTML Table to ADO.NET DataTable.
How to extract values from HTML Table and insert into ADO.NET DataTable?
The view is based on the following model
public class LeaveBalanceViewModel
{
public LeaveBalanceViewModel()
{
this.EmployeeDetail = new EmployeeDetails();
this.LeaveBalanceDetail = new LeaveBalanceDetails();
this.LeaveDetailsList = new List<LeaveBalanceDetails>();
}
public EmployeeDetails EmployeeDetail { get; set; }
public LeaveBalanceDetails LeaveBalanceDetail { get; set; }
public List<LeaveBalanceDetails> LeaveDetailsList { get; set; }
}
In order to bind to a model on post back, the name attributes of the form controls must match the model properties. Your use of a foreach loop does not generate the correct name attributes. If you inspect the html you will see multiple instances of
<input type="text" name="item.LeaveType" .../>
but in order to bind to your model the controls would need to be
<input type="text" name="LeaveDetailsList[0].LeaveType" .../>
<input type="text" name="LeaveDetailsList[1].LeaveType" .../>
etc. The easiest way to think about this is to consider how you would access the value of a LeaveType property in C# code
var model = new LeaveBalanceViewModel();
// add some LeaveBalanceDetails instances to the LeaveDetailsList property, then access a value
var leaveType = model.LeaveDetailsList[0].LeaveType;
Since your POST method will have a parameter name (say model), just drop the prefix (model) and that's how the name attribute of the control must be. In order to do that you must use either a for loop (the collection must implement IList<T>)
for(int i = 0; i < Model.LeaveDetailsList.Count; i++)
{
#Html.TextBoxFor(m => m.LeaveDetailsList[i].LeaveType)
....
}
or use a custom EditorTemplate (the collection need only implement IEnumerable<T>)
In /Views/Shared/EditorTemplates/LeaveBalanceDetails.cshtml
#model yourAssembly.LeaveBalanceDetails
<tr>
<td>#Html.TextBoxFor(m => m.LeaveType)</td>
....
</tr>
and then in the main view (not in a loop)
<table>
.... // add headings (preferably in a thead element
<tbody>
#Html.EditorFor(m => m.LeaveDetailsList)
</tbody>
</table>
and finally, in the controller
public ActionResult Edit(LeaveBalanceViewModel model)
{
// iterate over model.LeaveDetailsList and save the items
}
With respect to your requirement, try this
jQuery(document).on("change", ".DDLChoices", function (e) {
var comma_ChoiceIds = '';
var comma_ChoicesText = '';
$('input[class="DDLChoices"]').each(function (e) {
if (this.checked) {
comma_ChoiceIds = comma_ChoiceIds + $(this).val() + ',';
comma_ChoicesText = comma_ChoicesText + $(this).parent('label').parent() + ',';
}
});
$('#ChoiceIds').val(comma_ChoiceIds);
$('#ChoiceText').val(comma_ChoicesText);
});
#using (Html.BeginForm("Actionname", "Controllername", FormMethod.Post, new { id = "frmChoices" }))
{
#Html.HiddenFor(m => m.ChoiceText, new { #id = "ChoiceText" })
#Html.HiddenFor(m => m.ChoiceIds, new { #id = "ChoiceIds" })
<div class="form-group">
<div>
<table>
<tr>
<th>Name</th>
<th>Selected</th>
</tr>
#foreach (var item in #Model.Choices)
{
<tr>
<td> <label>#item.ChoicesText</label> </td>
<td> <input class="DDLChoices" value="#item.ChoiceIds" type="checkbox" /></td>
</tr>
}
</table>
</div>
<input type="button" value="Submit" onclick="return ChoicesPoster.passChoices()"
</div>
}
I have been using simple and clean access to removing items in a table populated from a breeze data service. Everything worked well until I started including navigation properties in the displayed table. Now whenever I use the delete functionality I get an error of Uncaught TypeError: Cannot read property 'BoardName' of null.
My database entities are set as:
public class SpecificAdapter
{
public int Id { get; set; }
public string PbaNumber { get; set; }
public int NumberOfPorts { get; set; }
public int AdapterId { get; set; }
public virtual Adapter Adapter { get; set; }
}
public class Adapter
{
public int Id { get; set; }
public string BoardName { get; set; }
public string DeviceId { get; set; }
public virtual ICollection<SpecificAdapter> SpecificAdapters { get; set; }
}
I am using a data service base in Breezejs this way:
var loadSpecificAdaptersTable = function () {
return em.executeQuery(breeze.EntityQuery.from('SpecificAdapters').expand('Adapter'));
};
Loaded in the view model like this:
adaptersDataService.loadSpecificAdaptersTable()
.then(function (data) { specificAdapters(data.results); })
.fail(function (error) { logger.error(error.message, "loadSpecificAdaptersTable failed during initialization"); });
I map it in the html like this:
<table class="table tablesorter">
<thead>
<tr>
<th data-bind="sort: {arr: specificAdapters, prop: 'Adapter().BoardName'}">Board Name</th>
<th data-bind="sort: {arr: specificAdapters, prop: 'PbaNumber'}">Pba Number</th>
<th data-bind="sort: {arr: specificAdapters, prop: 'NumberOfPorts'}"># Ports</th>
<th data-bind="sort: {arr: specificAdapters, prop: 'FirmwareVersion'}">Firmware Version</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: specificAdapters">
<tr>
<td data-bind="text: Adapter().BoardName"></td>
<td data-bind="text: PbaNumber"></td>
<td data-bind="text: NumberOfPorts"></td>
<td data-bind="text: FirmwareVersion"></td>
<td>
<button data-bind="click: $parent.editSpecificAdapter" class="btn">Edit</button>
</td>
<td>
<button data-bind="click: $parent.deleteSpecificAdapter" class="btn">Delete</button>
</td>
</tr>
</tbody>
</table>
Previous to Adapter().BoardName being added as a reference in the table, I could click the delete button and everything worked. Now I get an error. The delete logic is as:
var deleteSpecificAdapter = function (item) {
item.entityAspect.setDeleted();
specificAdapters.remove(item);
};
The error is thrown upon item.entityAspect.setDeleted(); being run. Does adding the data-binding to Adapter().BoardName change the item variable in a way that doesn't map back well enough to use? Do I need to have different logic for determining the actual item or do I need to bind the click event differently in order to get the specific, non-remapped item from the foreach?
By binding to the BoardName the way you are it is creating a timing issue. Since the value is being cleared before the parent context updates fully it throws an error. There are a few ways to get around this -
<td data-bind="with: Adapter"><span data-bind="text: BoardName"></span></td>
This will only bind to the BoardName property when adapter is populated
<td data-bind="if: Adapter"><span data-bind="text: BoardName"></span></td>
This will only evaluate the inner-span's data-binding when Adapter has a value.
in my MVC 4 web apps, I have a model data with a jQuery tab table, in each tab, I have some embedded tables which works as one to many relationship with this main tab table, in main tab table, I have a button ("Add"), when click it, a row of data field beside it with the data user entered will be added to the children table (which is a partialview) embedded in one of tab table, now, the code behind the button and controller saved the data to table, but the current embedded does not updated to show the new added table data at all. I appreciate anybody can help me out this problem.
This is the javascript behind Add button,
function AddMeal() {
//Build your Product
var product = { "MDate": $("#MDate").val(), "MRegion": $("#MRegion").va(), "MBAmount": $("#MBAmount").val(),
"MLAmount": $("#MLAmount").val(), "MDAmount": $("#MDAmount").val(), "FINtravelID": $("#FINtravelID").val() };
$.post('#Url.Action("Edit1","Travel")', product, function (data) {
if (data == null) {
location = location.href;
}
else {
//Populate your MealTable element with the results of your Partial View
$('#MealTable').html(data);
}
});}
This is controller:
[HttpPost]
public ActionResult Edit1( Meal product)
{
db.Meals.Add(product);
db.SaveChanges();
ViewBag.SaveMeal = "1";
return PartialView("_MealPartial", product);
}
this is the razor view related to this question,
#model Travelmvc.Models.FINtravel
<input type="button" value="Add" id="AddButton1" onclick="AddMeal()"/>
<div id="MealPartial">
#Html.Partial("~/Views/Shared/_MealPartial.cshtml")
</div>
This is partialview details codes:
#model Travelmvc.Models.FINtravel
<table class="MealT" id="MealTable" >
#if (Model != null) {
if (Model.Meals != null){ //here this line code always returns null, that is why the
read data created in SQL table can not display in the loop
foreach (var item in Model.Meals)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.MDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.MRegion)
</td>
<td>
#Html.DisplayFor(modelItem => item.MBAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MLAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MDAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MTAmount)
</td>
<td>
#Html.DisplayFor(modelItem => item.MProduct)
</td>
<td>
#Html.DisplayFor(modelItem => item.MRC)
</td>
<td>
#Html.DisplayFor(modelItem => item.MLocation)
</td>
<td>
#Html.DisplayFor(modelItem => item.MPOC)
</td>
<td>
aa
</td>
<td>
ff
</td>
</tr>
}
...
and this is the model of FINtravel and Meal.
public class FINtravel
{
public int FINtravelID { get; set; }
public string ClaimNo { get; set; }
public virtual ICollection<Meal> Meals { get; set; }
}
public class Meal
{
public int MealID { get; set; }
public int FINtravelID { get; set; }
public virtual FINtravel FINtravel { get; set; }
}
So my question is why the meal data created in Edit1 controller not display in razor view (partialview)? But I can see the data was saved in SQL table from back end.
can anybody help me out of this problem? thank a lot in advance.
you can try change your controller code like this:
public ActionResult Edit1( Meal product)
{
db.Meals.Add(product);
db.SaveChanges();
product = db.Meals.Find(product.MealID);//get saved product with filled property FINtravel
ViewBag.SaveMeal = "1";
return PartialView("_MealPartial", product.FINtravel);
}
fintravel = db.FINtravel.Find(product.FINtravelID);//
return PartialView("_MealPartial", fintravel);
solves the problem, thanks for all helps in this thread