I have a view that I had to change to add and save multiple start and end dates. I have a jQuery/jQuery-UI(datetimepicker) call that will dynamically create more input fields based on what the user wants to enter (code given below). I don't really now how to pass all the dates to the controller because the viewModel is looking for just one StartDate and EndDate. All the Id's are incremented and the class name is auto generated to hasDatePicker from the Jquery-UI plugin. I'm thinking that I can someway changing the ViewModel type into List<> might help in some sort of way but really I don't know best practices and what other options are out there.
Let me know what other information is needed.
HTML
<div class="field_wrapper2">
<table>
<tr>
<td class="DisplayFieldName">
# of start and end dates to create
</td>
<td class="DisplayFieldData" style="padding: 0px 0px 0px 5px;">
<input type="text" name="numOfDates" id="numOfDates" value="0" />
<button class="numOfDatesBtn" id="numOfDatesBtn" type="button">Add Dates</button>
</td>
</tr>
</table>
</div>
JavaScript
$(function() {
function makenewDateTimePicker(i, aObj) {
var startRow = $("<tr>").appendTo(aObj);
var startLabel = $("<td>").html("<label for='start-date-" + i + "'>Start:</label>").appendTo(startRow);
var startInput = $("<td>").html("<input type='text' id='start-date-" + i + "' />").appendTo(startRow);
var endRow = $("<tr>").appendTo(aObj);
var endLabel = $("<td>").html("<label for='end-date-" + i + "'>End:</label>").appendTo(startRow);
var endInput = $("<td>").html("<input type='text' id='end-date-" + i + "' />").appendTo(startRow);
}
$("#numOfDatesBtn").click(function() {
var num = parseInt($("#numOfDates").val());
var c = $("input[id^='start-date']").length;
c++;
for (c; c <= num; c++) {
makenewDateTimePicker(c, $(".field_wrapper2"));
}
});
});
ViewModel
public class DatePeriodViewModel
{
public Nullable<DateTime> StartDate { get; set; }
public Nullable<DateTime> EndDate { get; set; }
}
Controller
[HttpPost]
public ActionResult CreateAuction(DatePeriodViewModel viewModel) {
}
Might not be the cleanest solution but you can try capturing the submit event on the browser and then appending an array of dates to the form, which you can then access on the controller.
$("form").submit(function () {
var dates = // create an array of the dates, try using $(".hasDatePicker").each(...)
$(this).append(dates); //append to the form
return true; //return true to proceed with the submitting chain
});
Related
I have a model with 34 numbered properties in it as shown below
Public Class ViewModel
{
public string RatingCategory01 { get; set; }
public string RatingCategory02 { get; set; }
public string RatingCategory03 { get; set; }
//...and so on until category #34
}
Rather than code an input for each category in Razor Pages, I would like to use a loop to iterate through all the categories and generate the appropriate control groups. I have tried the code below:
<tbody>
#for (var i = 1; i < 35; i++)
{
string n;
#if (i > 0 && i < 10)
{
n = "RatingCategory0" + i.ToString();
}
else
{
n = "RatingCateogry" + i.ToString();
}
<tr>
<td>
<label asp-for="#string.Format("RatingCategory" + n)" class="control-label"></label>
</td>
<td>
<select asp-for="#string.Format("RatingCategory" + n)" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</td>
<td>
<input asp-for="#string.Format("RemedialTime" + n)" class="form-control" />
</td>
</tr>
}
</tbody>
When I build the project and navigate to the page, I get this error:
InvalidOperationException: Templates can be used only with field
access, property access, single-dimension array index, or
single-parameter custom indexer expressions.
I'm not sure if I am on the right track here. I would really like to create a loop to generate these inputs so make future maintenance and changes easier. It's probably pretty obvious from my code/question that I am pretty new to this, so any help is appreciated.
EDIT TO ADD SOLUTION:
I used the solution provided by Ed Plunkett which I have checked below. I altered it a bit and ended up creating a new class called 'Rating' because I found that in practice I needed a more complex object. Inside my view is now
public List<Rating> Ratings = { get; set; }
In the controller, I use a loop to add as many empty ratings as I need to the list depending on the number I need.
for (var i = 0; i < 34; i++)
{
vm.Ratings.Add(new Rating());
}
Though this will likely be updated to use something other than a hard-coded number as the application evolves.
Finally, I used a loop in the view to create a group of controls for every Rating in my List. In this case it is a TableRow containing different controls in different columns:
#for (var i = 0; i < Model.Ratings.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.Ratings[i].Category)
</td>
<td>
<div class="form-group">
<select asp-for="Ratings[i].RatingValue" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</div>
</td>
<td>
<input asp-for="Ratings[i].RemediationMinutes" class="form-control" />
</td>
</tr>
}
I've found that the data in this group of inputs can be bound as a List by simply including
List<Rating> Ratings
in the parameters on whichever method runs when the form is submitted.
This is what you want instead of those 34 properties and their implied 34 RemedialTime siblings:
public List<String> RatingCategory { get; set; } = new List<String>();
public List<String> RemedialTime { get; set; } = new List<String>();
If you have 34 of something and the names differ only by an index number, that's a collection, not 34 distinct properties with sequentially numbered names. Then you can enumerate the 34 items with a foreach loop, or index them individually as RatingCategory[0] through RatingCategory[33]. In C#, collection indexes start at zero, so the first one is 0 and the thirty-fourth one is 33. You get used to it.
You should also look up what String.Format() does. String.Format("Foo" + 1) is exactly the same as "Foo" + 1.
You could convert your model class to dictionary;
var viewModel = new ViewModel()
{
RatingCategory01 = "a",
RatingCategory02 = "b",
RatingCategory03 = "c"
};
var dictionaryModel = viewModel.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(prop => prop.Name, prop => prop.GetValue(viewModel, null));
Then you can iterate the dictionary in the view.
I have the following in a controller :
outputmodel.Add(new SP_RESULTS.RS_Plans()
{
id = Convert.ToDecimal(SPOutput["id"]),
name = Convert.ToString(SPOutput["name"]),
code = Convert.ToString(SPOutput["code"]),
from = Convert.ToDateTime(SPOutput["from"]),
to = Convert.ToDateTime(SPOutput["to"]),
days = Convert.ToDecimal(SPOutput["days"]),
type_id = convert.YoString(SPOutput["type_id"]),
package = Convert.ToString(SPOutput["package"]),
day = Convert.ToDecimal(SPOutput["day"]),
charge = SPOutput["charge"] as decimal?,
type = Convert.ToString(SPOutput["type"]),
percentage= SPOutput["percentage"] as decimal?,
taxes = Convert.ToDecimal(SPOutput["taxes"]),
order = Convert.ToDecimal(SPOutput["order"]),
level = SPOutput["level"] as decimal?,
Column15 = Convert.ToDecimal(SPOutput[15]),
type_order = (SPOutput["type_order"]) as decimal?,
adults = SPOutput["adults"] as decimal?,
});
var order = outputmodel.OrderBy(c => c.from);
ViewData["RS_Output"] = order;
grabbing output from an MS SQL stored procedure and storing in a viewdata (ordered by the FROM date).
My HTML has the following line to start to build the table
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
//basic <tr> <td> </td> </tr> table setup, using #item.variablename to pull info from the viewdata.
}
The output I am trying to achieve is for every TYPE under CODE, where the from date => current date, list the room type /package name etc.
and the output I am getting is
what I am trying to get is
What I think I need is a foreach after the current foreach, but I cannot for the life of me figure it out in my head.
I've changed the
var order line in my controller to now read
var order = outputmodel.OrderBy(c => c.rate);
..and I've put the HTML table create code in an if loop
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
if (item.to >= DateTime.now)
{
//basic <tr> <td> </td> </tr> table setup, using #item.variablename to pull info from the viewdata.
}
}
.. but, as I say, I am stumped.
I think I need another foreach within the newly created if loop, but I cannot figure out how.
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
if (item.to >= DateTime.now)
{
//other table headers/data
<tr>
<td>
#item.type
</td>
</tr>
<tr>
<td>
Room Type
</td>
<td>
Package / Service
</td>
<td>
Availablility
</td>
<td>
Charge
</td>
<td>
PAX
</td>
<td>
Level
</td>
</tr>
<tr>
==> #foreach (subitem = item.type)
==> {
==> foreach (item.type)
==> {
<td>
#item.type_id
</td>
<td>
#item.package
</td>
<td>
#item.Column15
</td>
<td>
#item.charge
</td>
<td>
#item.adults
</td>
<td>
#item.level
</td>
==> }
==> }
</tr>
}
}
can someone please advise?
thanks
UPDATE:
Hi, what I found worked was, if I create a variable called string previous_type =" " , and another called decimal previous_id =0 , then, in the view, I can amend with
if (item.to >= item.checkdate)
{
if ((previous_id != item.id) && (previous_type != item.type.ToString()) )
{
//some more code
if (item.type.ToString().Equals(previous_type) == false)
{
previous_type = item.type.ToString();
previous_date_from = item.date_from;
}
//etc
}
Thanks everyone for their help
OK, I think what you want is to first group the data, then show a table which then shows 'sub-tables' for each type of accomodation?
if so, then yes you can do this with nested foreach loops, but you'd still be better off strongly typing your view and doing the grouping stuff in the controller (or possibly better in some sort of service layer so it can be more easily tested/re-used)... but to get you started, something like this:
Models:
//Raw data
public class DataRowModel
{
public int Id { get; set; }
public string Class{ get;set;}
public string Description { get; set; }
public DateTime BookingDate { get; set; }
}
//Grouped data
public class GroupedDataRowModel
{
public string Class { get; set; }
public IEnumerable<DataRowModel> Rows { get; set; }
}
//View model
public class DataRowsViewModel
{
public IEnumerable<GroupedDataRowModel> Results { get; set; }
}
Controller Action:
public ActionResult TestData()
{
var PretendDatabaseCall = new List<DataRowModel>
{
new DataRowModel{
Id =1,
BookingDate =new DateTime(2017,1,1),
Description ="Booking 1",
Class="Room"
},
new DataRowModel{
Id =2,
BookingDate =new DateTime(2017,2,1),
Description ="Booking 2",
Class="Room"
},
new DataRowModel{
Id =3,
BookingDate =new DateTime(2017,3,1),
Description ="Booking 3",
Class="Suite"
},
new DataRowModel{
Id =4,
BookingDate =new DateTime(2017,4,1),
Description ="Booking 4",
Class="Room"
},
};
//We can now get the data from the database. We want to group by class so we can
//get a summary of items by class rather than a big flat list. Most LINQ to SQL implementations
//(e.g. Entity Framework) when working with Raw entities could convert this to SQL so the SQL server
//does the grouping, but if not it can happen in memory (get all records, then standard LINQ does it on
//the complete list)
var dataGroupedByClass = PretendDatabaseCall
//Minor Edit: apply filtering here not in the view!
.Where(x=>x.BookingDate >= Datetime.Now)
//Group by class.
.GroupBy(x => x.Class)
//for each class, get the records.
.Select(grpItem => new GroupedDataRowModel()
{
//'key' is the thing grouped by (class)
Class = grpItem.Key,
//grpItem has all the rows within it accessible still.
Rows = grpItem.Select(thisRow => thisRow)
});
var model = new DataRowsViewModel
{
Results = dataGroupedByClass
};
return View("~/Views/Home/TestData.cshtml", model);
}
And View:
#* Strongly typed view. saves any casting back and forth.*#
#model SimpleWeb.Models.DataRowsViewModel
#{
ViewBag.Title = "TestData";
}
<h2>TestData</h2>
<table>
<thead></thead>
<tbody>
#foreach (var groupEntry in Model.Results)
{
#*Add single row with just the class...*#
<tr><td>#groupEntry.Class</td></tr>
#*Header row for each class of booking*#
<tr>
<td>Id</td>
<td>Description</td>
<td>Date</td>
</tr>
foreach (var row in groupEntry.Rows)
{
#*add new row for each actual row*#
<tr>
<td>
#row.Id
</td>
<td>
#row.Description
</td>
<td>
#row.BookingDate
</td>
</tr>
}
}
</tbody>
</table>
This produces Data like I think you want:
Room
Id Description Date
1 Booking 1 01/01/2017 00:00:00
2 Booking 2 01/02/2017 00:00:00
4 Booking 4 01/04/2017 00:00:00
Suite
Id Description Date
3 Booking 3 01/03/2017 00:00:00
Obviously you want the 'Room' and 'Suite' parts to contain more information, but this should hopefully help get you started?
I have 3 models - School, Classrooms & Courses.
A school can have many classrooms, and a classroom can have many courses taught in it.
public class School
{
public int SchoolId { get; set; }
public string SchoolName { get; set; }
public List<Classroom> Classrooms { get; set; }
}
public class Classroom
{
public int ClassroomId { get; set; }
public string Room { get; set; }
public List<Course> Courses { get; set; }
public virtual int SchoolId { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual int ClassroomId { get; set; }
}
I want to create CRUD actions for School, where I can add 0 to many classrooms and within each classroom create 0 to many courses all in one form. Allowing the user to dynamically add classrooms and courses within those classrooms all within creating a school
The way that I'm accomplishing this right now is hard coding (where School is the #Model):
<div class="col-md-10">
<input asp-for="#Model.SchoolName" class="form-control" />
</div>
<div class="col-md-10">
<input asp-for="#Model.Classroom[0].Room" class="form-control" />
</div>
<div class="col-md-10">
<input asp-for="#Model.Classroom[0].Course[0].CourseName" class="form-control" />
</div>
<div class="col-md-10">
<input asp-for="#Model.Classroom[0].Course[1].CourseName" class="form-control" />
</div>
<div class="col-md-10">
<input asp-for="#Model.Classroom[0].Course[3].CourseName" class="form-control" />
</div>
I want the user to be able to add more classrooms and more courses or have the ability to remove them dynamically on the form.
You can use a partial view and JQuery to create dynamic rows in an html table.
I had a asset transfer form that required at least one line item. I placed a select list for the assets and a button to add the asset and it's details (the line item) to an html table via a partial view.
Partial View
#model ITFixedAssets.Models.TransferLineItem
#{//this helps with naming the controls within the main view/page
Models.TransferLineItem TransferLineItem = new Models.TransferLineItem();
}
<tr>
<td>
<span class="glyphicon glyphicon-trash" style="padding-right:5px;" data-action="removeItem" onclick="RemoveRow(this);"></span>
</td>
<td>
<label asp-for="#TransferLineItem.Id" class="form-control" id="row" style="height:34px;margin-top:5px;"></label>
</td>
<td>
<input asp-for="#TransferLineItem.Asset" class="form-control" />
</td>
<td>
<select asp-for="#TransferLineItem.Condition" class="form-control">
<option>Please Select</option>
<option value="New">New</option>
<option value="Good">Good</option>
<option value="Bad">Bad</option>
</select>
<span asp-validation-for="#TransferLineItem.Condition" class="text-danger"></span>
</td>
<td>
<input asp-for="#TransferLineItem.AssetDescription1" class="form-control" value="#ViewBag.AssetDescription1" />
</td>
<td>
<input asp-for="#TransferLineItem.AssetDescription2" class="form-control" />
</td>
<td>
<input asp-for="#TransferLineItem.InventoryNum" class="form-control" />
</td>
<td>
<input asp-for="#TransferLineItem.SerialNumber" class="form-control" />
</td>
</tr>
When the 'Add Asset' button is clicked it calls a JS function, which uses an ajax GET to run a 'code behind' function that returns the partial view, which is basically a skeleton of a row to be placed into the html table.
JS Function and 'Code Behind' function that returns the partial view.
function AddRow() {
$.ajax({
type: "GET",
url: "Create?handler=AddLineItemRow"
}).success(function (data) {
AddNewRow(data);
});
}
public PartialViewResult OnGetAddLineItemRow()
{
PartialViewResult partialViewResult = new PartialViewResult();
partialViewResult.ViewName = "_LineItem";
return partialViewResult;
}
If successful then the partial view ('data') is passed to another JS function that adds the row to the table and alters the generic id's and names of the controls to the appropriate 'array' style that is needed when the form is submitted in order to save the line items into the database.
//appends the transfer line item table with a new row (partial view)
function AddNewRow(data)
{
//******************************************************
//*************add the new to the table**********************
//append the table with the new row
$('#LineItems').append(data);
//******************************************************
//get value of asset drop down
var asset = $("#assetList").val();
//******************************************************
//*****add the row number to the id values of the controls********
//get the length of the table for new row #
//(subtract 1, not sure why unless it's adding the headers into the count)
var rowNum = $("#LineItems tr").length - 1;
//build new name to add to the controls
var nameIndex = rowNum - 1;
var newName = "[" + nameIndex + "].";
//get the row label
var lblRow = $("#row");
//create new id for the row label
var newId = "row_" + rowNum;
//apply new id
lblRow.attr("id", newId);
//get the last row in the table
var lastRow = $("#LineItems").find("tr").last();
////format id values for the input controls
//and add names for the controls
lastRow.find("input").each(function () {
// get id of this input control
var ctrl = $(this).attr("id");
//concatenate the row number to the id
var newId = ctrl + "_" + rowNum;
//assign new id to control
$(this).attr("id", newId);
//add the index to the control'
$(this).attr("name",$(this).attr("name").replace(".", newName));
});
//update the select list (for condition) id value
var selectControl = lastRow.find("select");
//get id of the select control
var scId = selectControl.attr("id");
//concatenate the row number to the id
newId = scId + "_" + rowNum;
//assign new id to control
selectControl.attr("id", newId);
//add new name to control
selectControl.attr("name", selectControl.attr("name").replace(".", newName));
//this ajax calls the function 'OnGetPopulateLineItemRow' in the code behind
//and passes the asset # and row # as parameters. the success function
//receives the return value from the OnGetPopulateItemRow function and
//passes that 'data' to the PopulateRow function below
$.ajax({
type: "GET",
url: "Create?handler=PopulateLineItemRow",
data: { _asset: asset },
//data: { _asset: asset, row: rowNum }
dataType: 'json',
success: function (data) {
PopulateRow(asset, data, rowNum);
}
});
}
After the new row has been added to the table and the names and id's are changed, another ajax 'GET' is used to call a 'code behind' function to to get the values for the controls in the new row (as can be seen in the above snippet).
Function that gets the item details of the Asset
public JsonResult OnGetPopulateLineItemRow(string _asset)
{
//stores named value pairs for the result
Dictionary<string, string> results = new Dictionary<string, string>();
//get record from the database for the specified asset (_asset)
var asset = from a in _context.Asset select a;
asset = asset.Where(a => a.Asset1 == _asset);
//get value for each field
var description1 = from d in asset select d.AssetDescription;
var description2 = from d in asset select d.AssetDescription2;
var inventoryNum = from d in asset select d.InventoryNo;
var serialNum = from s in asset select s.SerialNo;
//add the name value pairs to the collection
results.Add("description1", description1.ToList()[0].ToString());
results.Add("description2", description2.ToList()[0].ToString());
results.Add("inventoryNum", inventoryNum.ToList()[0].ToString());
results.Add("serialNum", serialNum.ToList()[0].ToString());
return new JsonResult(results);
}
Finally these results are passed back to the success function and JQuery is used to populate the values
function PopulateRow(asset, data,rowNum) {
$("#row" + "_" + rowNum).text($("#LineItems tr").length - 1);
$("#TransferLineItem_Asset" + "_" + rowNum).val(asset);
$("#TransferLineItem_AssetDescription1" + "_" + rowNum).val(data["description1"]);
$("#TransferLineItem_AssetDescription2" + "_" + rowNum).val(data["description2"]);
$("#TransferLineItem_InventoryNum" + "_" + rowNum).val(data["inventoryNum"]);
$("#TransferLineItem_SerialNumber" + "_" + rowNum).val(data["serialNum"]);
//reset drop down
$('#assetList').prop('selectedIndex', 0);
}
Just as a side note. Until recently I used ASP.net webforms. I'm still trying to understand how to accomplish what I considered to be simple things with server side coding that really, to me, seem to be more complicated with MVC and Razor.
I am not sure if this is possible, and have not found any similar questions on this.
We have an Edit View that is NOT for a single record, but for the multiple members of a "parent" record. These "child" record need to be edited together (at the same time). ... if possible.
One field in each of these "child" records is a reference to another table, so a select list is required. We use DropDownListFor in all of our standard Edit Views, and the single record edits fine.
Our model for this issue is :
[Display(Name = "Team Member")]
public int Contact_ID { get; set; }
[Display(Name = "Team Member")]
public String Contact_Name { get; set; }
[Display(Name = "Type/Role")]
public int MemberTypeLookUp_ID { get; set; }
[Display(Name = "Type/Role")]
public String MemberTypeValue { get; set; }
[Display(Name = "Type/Role")]
public LookUpList MemberTypeLookUp { get; set; }
We retrieve the first 4 fields via a select from a database table. Straightforward and OK..
Our code to set up the DropDownListFor is :
(edit : new code added within the foreach() loop to manually set the .Selected property of the relevant option within each list to true. This still does not translate over to the actual displayed View...)
foreach (TeamEditViewItem tevi in this.members)
{
tevi.MemberTypeLookUp = new LookUpList("TeamMemberType");
foreach (SelectListItem item in tevi.MemberTypeLookUp.list)
{
if (item.Value == tevi.MemberTypeLookUp_ID.ToString())
{
item.Selected = true;
break;
}
}
}
For completion of this question, the LookUpList code is :
public class LookUpList
{
public SelectList list;
// Return all Active LookUp entries for the passed-in Category.
public LookUpList(String Category)
{
WorkpointContext _db = new WorkpointContext();
int Customer_ID = _db.GetCustomer_ID();
IList<LookUp> items = (from lookup in _db.LookUp
where (lookup.Category == Category)
&& (lookup.IsActive == true)
orderby lookup.DisplayOrder ascending
select lookup).ToList();
this.list = new SelectList(items, "ID", "Value");
}
}
As mentioned, the LookUpList code is fine for a single record on a standard Edit View.
After rendering the page, we get the multiple "child" records listed, however the DropDown List does not hold the existing value for each record. (This is an EDIT not a Create, so values have already been assigned via defaults and other logics - not via DropDown lists on the Create View.
When viewing the source of the page, I can see that each of the DropDown Lists have their own ID.
I have a feeling that our issue is due to the multiple DropDownListFor objects on the page, but cannot figure out WHAT the issue is and WHY we have the issue.
Our View has simple code for the DropDownList :
#Html.DropDownListFor(model => model.members[i].MemberTypeLookUp_ID, Model.members[i].MemberTypeLookUp.list, "--Select--")
#Html.ValidationMessageFor(model => model.members[i].MemberTypeLookUp_ID)
The third parameter has been added because we were always getting the first option in the DropDown Lists and needed to determine if there was a value or not.
We are constantly getting the "--Select--" option displayed in the DropDown Lists, which is a placeholder and not a valid option - therefore the Validation Message is displayed.
(Edit) I have added the complete Edit View cshtml code :
#model WebWorkPoint.Models.TeamEditView
<h3>Edit Team</h3>
#using (Html.BeginForm()) {
<fieldset>
#if (Model.members.Count>0)
{
<table>
<!-- table headings -->
<thead>
<tr>
<td style="text-align:center; border-bottom: 1px solid black; " >
<div class='editor-label'>
#Html.LabelFor(m => m.members.First().Contact_Name)
</div>
</td>
<td class="spacer-border"> </td>
<td style="text-align:center; border-bottom: 1px solid black; " >
<div class='editor-label'>
#Html.LabelFor(m => m.members.First().MemberTypeValue)
</div>
</td>
</tr>
</thead>
<!-- table rows -->
<tbody>
#for (int i = 0; i < Model.members.Count; i++)
{
<tr>
<td style="text-align:center; " >
#Html.HiddenFor(m => m.members[i].Contact_ID)
<div class="editor-field">
#Html.EditorFor(m => m.members[i].Contact_Name)
#Html.ValidationMessageFor(model => model.members[i].Contact_Name)
</div>
</td>
<td class="spacer"></td>
<td style="text-align:center; " >
<div class="editor-field">
#Html.DropDownListFor(model => model.members[i].MemberTypeLookUp_ID, Model.members[i].MemberTypeLookUp.list, "--Select--")
#Html.ValidationMessageFor(model => model.members[i].MemberTypeLookUp_ID)
</div>
</td>
</tr>
}
</table>
}
else
{
<p>There are currently no team members defined.</p>
}
<p>
<input type="submit" value="Update Team" />
#{
sAction = "/" + Model.TableNameValue + "/" + Model.TableNameValue + "Show/" + Model.TableRecord_ID.ToString();
sLinkText = "Cancel";
}
<button type="button" onclick="location.href='#sAction'" >#sLinkText</button>
</p>
</fieldset>
}
(end Edit)
Can anyone shed some light into our issue ? Thank you in advance for any help.
After reading this answer on Stack Overflow , we decided to try the same kind of resolution.
As it turns out, the FULL resolution went as follows :
We still needed to set up the LookUpList in the setup code (but did not need to attempt any select code) :
// other code above ...
foreach (TeamEditViewItem tevi in this.members)
{
tevi.MemberTypeLookUp = new LookUpList("TeamMemberType");
}
The LookUpList() code creates the SelectList as per the original issue/question - no changes required there.
We also needed to replace the DropDownListFor() call on the Edit View :
#Html.DropDownListFor(model => model.members[i].MemberTypeLookUp_ID, new SelectList(Model.members[i].MemberTypeLookUp.list, "Value", "Text", Model.members[i].MemberTypeLookUp_ID), "--Select--")
It seemed repetitive or redundant, but this is what was required. There may be something we could do to clean it further, but it "ain't broke" now, so why try to fix it ?
I must say thank you to #Stephen Muecke and #Mario Lopez for their input, to get us investigating and thinking further afield from what we were doing. Also, thank you to #ataravati for resolving the other issue linked above, to get us to try something else.
Hopefully our issue and resolution might help other coders out there ...
I think what is happening is that all the dropwdowns are being generated with the same Id = MemberTypeLookUp_ID. What I would do is creating a partial view for the child and call it from the main view inside a foreach and pass to this partial view only the child model that has to be populated for and not the whole parent model.
I'm working in MVC and was trying to use jquery to perform the following, if there is an easier way i'd be happy to hear it...
I'm looking to have my form create dynamic input to produce something for example that could look like this: *i won't know how many subcat's they are going to enter nor how many main categories.
category0 <btnInsertSubCat0>
subcat0.0
subcat0.1
subcat0.2
category1 <btnInsertSubCat1>
subcat1.0
subcat1.1
<btbInsertNewCat>
I can produce a button that appends textboxes one after another as well as a button next to it but even if I use the .live attribute I can't get the subcat button to fire. Changed code around a lot and looked at different tutorial to no avail, fig. this would be semi easy to do?
jQuery(document).ready(function () {
var procID = 0;
var orgID = 0;
$('.clickme').live('click', function () {
var newItem = $("<input type='text' name='Procedure[" + procID + "]' value='Procedure[" + procID + "] />");
var newLabel = $("<br /><label id='Label[" + procID + "]' >ProcedureID: [" + procID + "]</label>");
var newDiv = $("<div class='objective'><b>Insert Objective</b>[" + procID + "." + orgID + "]</div>");
$("#procedureHolder").append(newLabel);
$("#procedureHolder").append(newItem);
$("#procedureHolder").append(newDiv);
procID++;
});
$('.objective').live('click', function () {
var newObj = $("<input type='text' id='Objective[" + (procID - 1) + "." + orgID + "]' >ObjectiveID: [" + (procID - 1) + "." + orgID + "]</label>");
$("#procedureHolder").append(newObj);
orgID++;
});
});
I edited my post, figured it out on my own how to utilize jquery to create an unlimited number of child dynamic textboxes. They all post back in the form collection as well. I figured out why the objectives weren't showing up, turns out i was declaring ID rather than Name. Thanks!
I've used the following approach for a dynamic search screen using ASP.NET MVC. Search options where managed in a database mapped to a product. This allowed the marketing team to tweak searching results and the search options in the admin section of the website. Essentially the approach was the following:
public class FormFieldCollection : List<FormField>
{
public string FormFieldType { get; set; }
}
public class FormField
{
public string Name {get;set;}
public string Type {get;set;}
public string Value {get;set;}
public bool IsChecked {get;set;}
}
public class FormFieldModel
{
public FormFieldCollection PaymentOptions { get; set; }
}
In the view generated from either custom Helper or use a foreach.
In your Controller action something like:
public ActionResult SomeActionMethod(FormCollection formCollection)
{
//search through the results, map back to class or loop through key value pairs.
}
View Code, obviously include some nice html markup to format the form.
#Model FormFieldCollection
#{
View.Title = "Form";
Layout = "~/Views/Shared/_defaultMaster.cshtml";
}
#foreach(var formField in Model){
<input type="#formField.Type" id="#formField.Name" name="#formField.Name" value=""#formField.Value"" />
}
#using (Html.BeginForm("SomeActionMethodAdd", "ControllerName", FormMethod.Post))
{
//Submit form to
Field Name: <input type="text" value="" /> * must be unique
<input type="submit" value="Submit" />
}
Syntax mightn't be perfect just going of memory for razor.