I have a very special problem and wasn't yet able to find anything to help me solving it.
I have a form which has a bunch of textboxes, dropdownlists and such which are posted back through the model attached in the view. However I also have a Grid which is dynamically populated through one of the DropDownLists. I want to parse the whole grid back to the forms-controller, but I have no clue how to do that.
An ajax-post doesn't work, because I have no access to the actual model. The only thing I could do is post the controls-values itself, but that approach is somewhat messy.
The model itself has a variable "Mappings" which is of the Type IEnumerable. This is the type I also bind to the grid. So there should be a way to somehow add the mappings inside of the grid to the model. I thought it might work automatically if I name the grid like the name of the property inside of the main model ("Mappings"), but that didn't work. So I am yet again back to square one.
Maybe you guys have an idea how to solve my Problem as I am somewhat out of ideas.
View:
#using (Html.BeginForm("Save", "Profile", FormMethod.Post, new { Id = "addProfileForm", novalidate = "false" }))
{
<ul id="progressbar">
<li class="active">Grundeinstellungen</li>
<li>CRM-Einstellungen</li>
<li>Mapping anlegen</li>
</ul>
<fieldset>
<h2 class="fs-title">Grundinformationen</h2>
#Html.LabelFor(model => model.Name, new { #class = "label req" })
#Html.TextBoxFor(model => model.Name, new { #class = "input" })
#Html.LabelFor(model => model.Description, new { #class = "label" })
#Html.TextAreaFor(model => model.Description, new { #class = "input", rows = "3", cols = "25" })
#Html.LabelFor(model => model.UserGroupId, new { #class = "label req" })
#(Html.Kendo().DropDownListFor(model => model.UserGroupId)
.OptionLabel("Bitte auswählen... ")
.BindTo(Model.UserGroupList)
)
<input type="button" name="next" class="next action-button" value="Weiter" style="display:block"/>
</fieldset>
<fieldset>
<h2 class="fs-title">CRM-Einstellungen</h2>
#Html.LabelFor(model => model.OrdnungsbegriffTypeId, new { #class = "label req" })
#(Html.Kendo().DropDownListFor(model => model.OrdnungsbegriffTypeId)
.OptionLabel("Bitte auswählen...")
.BindTo(Model.OrdnungsbegriffTypeList)
)
#Html.LabelFor(model => model.CrmTypeId, new { #class = "label req" })
#(Html.Kendo().DropDownListFor(model => Model.CrmTypeId)
.Name("CrmTypeId")
.OptionLabel("Bitte auswählen...")
.BindTo(Model.CrmTypeList)
.Events(e =>
e.Select("onCrmTypeSelect"))
)
<br />
<input type="button" name="previous" class="previous action-button" value="Zurueck" />
<input type="button" name="next" class="next action-button" value="Weiter" />
</fieldset>
<fieldset>
<h2 class="fs-title">Mapping anlegen</h2>
#(Html.Kendo().Upload()
.Name("upMappingFile")
.Multiple(false)
.ShowFileList(true)
.TemplateId("fuCsvTemplate")
.Async(a => a
.AutoUpload(true)
.Save("SaveFile", "Mapping")
.Remove("RemoveFile", "Mapping")
)
.Enable(false)
.Events(e => e
.Success("onSuccess"))
.Messages(m => m
.Select("Durchsuchen...")
.HeaderStatusUploading("Uploading...")
.HeaderStatusUploaded("Fertig"))
)
<div id="gridContainer">
</div>
<div id="appendTo" class="k-block"></div>
<input type="button" name="previous" class="previous action-button" value="Zurueck" />
<input type="submit" name="submit" class="submit action-button" value="Speichern"/>
</fieldset>
#Html.Kendo().Notification().Name("crmTypeNotification")
}
Ajax-Post for dynamically creating the grid:
function onSuccess(e) {
var gridNotification = $("#gridNotification").data("kendoNotification");
var crmType = $("#CrmTypeId").data("kendoDropDownList");
var csvHeadRow = e.response.csvHeadRow
var url = '#Url.Action(MVC.Mapping.ReturnMappingGrid())';
$.ajax({
type: "POST",
url: url,
data: JSON.stringify({ csvHead: csvHeadRow, crmType: crmType.text() }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
$('#gridContainer').html(data);
gridNotification.show("Die Auswahl der jeweiligen Eigenschaft erfolgt durch Klicken des jeweiligen Eigenschaft-Felds!")
var container = $(gridNotification.options.appendTo);
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
debugger;
console.log(xmlHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
},
async: false
});
}
Partial View Grid:
#(Html.Kendo().Grid<DAKCrmImportModel.Model.Entities.Base.CrmMapping>().Name("Mappings").BindTo(Model)
.Columns(c =>
{
c.Bound(m => m.CsvColumn).Title("Spalte").Width(300);
c.Bound(m => m.CrmProperty).EditorTemplateName("PropertyId");
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(20)
.ServerOperation(false)
.Model(m =>
{
m.Id(ma => ma.Id);
m.Field(ma => ma.Id).Editable(false);
})
)
)
CrmProfile-model:
public class CrmProfile
{
public CrmProfile()
{
this.Mappings = new HashSet<CrmMapping>();
this.Jobs = new HashSet<CrmJob>();
}
[Column(Order = 1)]
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "Bezeichnung")]
public string Name { get; set; }
[Display(Name = "Beschreibung")]
public string Description { get; set; }
[Required]
public string CreatedBy { get; set; }
[Column(Order = 2)]
[ForeignKey("UserGroup")]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
[Required]
[Display(Name = "Usergruppe")]
public int UserGroupId { get; set; }
public UserGroup UserGroup { get; set; }
[Column(Order = 3)]
[ForeignKey("CrmType")]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
[Required]
[Display(Name = "Einspielungstyp")]
public int CrmTypeId { get; set; }
public CrmImportType CrmType { get; set; }
[Column(Order = 4)]
[ForeignKey("OrdnungsbegriffType")]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
[Required]
[Display(Name = "Ordnungsbegrifftyp")]
public int OrdnungsbegriffTypeId { get; set; }
public OrdnungsbegriffType OrdnungsbegriffType { get; set; }
public byte[] SourceFile { get; set; }
public string SourceFileName { get; set; }
public string[] SourceFileHeadRow { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
public string LastUpdatedBy { get; set; }
public DateTime? LastUpdatedAt { get; set; }
public DateTime? LastUsedAt { get; set; }
public ICollection<CrmMapping> Mappings { get; set; }
public ICollection<CrmJob> Jobs { get; set; }
[NotMapped]
public IEnumerable<SelectListItem> UserGroupList { get; set; }
[NotMapped]
public IEnumerable<SelectListItem> CrmTypeList { get; set; }
[NotMapped]
public IEnumerable<SelectListItem> OrdnungsbegriffTypeList { get; set; }
}
CrmMapping-model:
public class CrmMapping
{
[Key]
[Column(Order = 0)]
public int Id { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
[ForeignKey("Profile")]
public int ProfileId { get; set; }
public CrmProfile Profile { get; set; }
[Required]
public string CrmProperty { get; set;}
[Required]
public string CsvColumn { get; set; }
[Required]
public int CsvIndex { get; set; }
[UIHint("PropertyId")]
[NotMapped]
public int PropertyId { get; set; }
[NotMapped]
public IEnumerable<CrmProperty> CrmProperties { get; set; }
}
[UPDATE]
I want to post the grid-data back with ajax, but I don't seem to get the right datatype on serverside.
function postProfileData() {
var url = '/Profile/Save';
$.ajax({
type: "POST",
url: url,
data: $("#addProfileForm").serialize() + "&Mappings=" + getMappings(),
success: function (data) {
debugger;
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
debugger;
console.log(xmlHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
},
async: false
});
}
function getMappings() {
var grid = $("#Mappings").data("kendoGrid");
debugger;
return grid._data;
}
[HttpPost]
public virtual ActionResult Save(CrmProfile crmProfile, List<CrmMapping> Mappings)
{
using (DAKCrmImportContext db = new DAKCrmImportContext())
{
crmProfile.CreatedAt = DateTime.Now;
crmProfile.CreatedBy = System.Web.HttpContext.Current.User.Identity.Name.Split('\\')[1];
try
{
db.CrmProfiles.Add(crmProfile);
db.SaveChanges();
}
catch (DbEntityValidationException e)
{
var vals = e.EntityValidationErrors;
throw;
}
}
return RedirectToAction("Add", "Mapping", crmProfile);
}
[UPDATE2] So the problem is likely that JavaScript doesn't like my Listtype (CrmMapping) and doesn't know what to do with it. I'm not quite sure why, but I think its's because its structure doesn't fit the normal id:value pair. Maybe the problem is that Id is 0 in all ListItems and I'll have to fill it manually, but CrmMapping is actually a model-class so I'd have to set it back to 0 eventually so I'd probably have to create a ViewModel just to parse the grid-data back.
public class CrmMapping
{
[Key]
[Column(Order = 0)]
public int Id { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
[ForeignKey("Profile")]
public int ProfileId { get; set; }
public CrmProfile Profile { get; set; }
[Required]
public string CrmProperty { get; set;}
[Required]
public string CsvColumn { get; set; }
[Required]
public int CsvIndex { get; set; }
[UIHint("PropertyId")]
[NotMapped]
public int PropertyId { get; set; }
[NotMapped]
public IEnumerable<CrmProperty> CrmProperties { get; set; }
}
Since you're creating the grid from the #model instead of using ajax calls for the datasource.Read, you could just create hidden inputs using a for loop, and as long as they're in the form tag they would post back.
Something like
#(Html.Kendo().Grid<DAKCrmImportModel.Model.Entities.Base.CrmMapping>().Name("Mappings").BindTo(Model)
.Columns(c =>
{
c.Bound(m => m.CsvColumn).Title("Spalte").Width(300);
c.Bound(m => m.CrmProperty).EditorTemplateName("PropertyId");
})
.Editable(editable => editable.Mode(GridEditMode.InCell))
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(20)
.ServerOperation(false)
.Model(m =>
{
m.Id(ma => ma.Id);
m.Field(ma => ma.Id).Editable(false);
})
)
)
#for (int i = 0; i < Model.Count; i++)
{
#Html.Hidden("Mappings[" + i.ToString() + "].Id", Model[i].Id)
#Html.Hidden("Mappings[" + i.ToString() + "].CsvColumn", Model[i].CsvColumn)
#Html.Hidden("Mappings[" + i.ToString() + "].CrmProperty", Model[i].CrmProperty)
}
If you edit any of the columns, the new values won't get posted back using this method so you should probably wire up the ajax datasource events to Read, Create, Edit and Delete if you want to be able to maintain the grid data correctly
Ok, after trying a bunch of things I finally found a solution. To do this I need a javscript-expension:
$.fn.serializeObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
This is needed to serialize the form as an object instead of posting the form itself.
The next thing we have to do is create a ViewModel which has properties for the model of the form we want to submit/post and the additional data we want to get into the controller-action.
using DAKCrmImportModel.Model.Entities.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DAKCrmImport.Models
{
public class CrmProfileAddVM
{
public CrmProfile Profile { get; set; }
public CrmMapping[] Mappings { get; set; }
}
}
We have to declare CrmMapping as Array here, because thats the datatype the Telerik-grid uses.
Now let's do the ajax-function which contains our ViewModel with the form-data (Profile) and the grid._data (Mappings).
function postProfileData() {
var url = '#Url.Action(MVC.Profile.Save())';
$.ajax({
type: "POST",
url: url,
//.serialize()
//data: $("#addProfileForm").serialize(), "crmMappingList": getMappings(),
contentType: 'application/json',
data: JSON.stringify({
Profile: $("form#addProfileForm").serializeObject(),
Mappings: getMappings()
}),
//data: { Mappings: getMappings() },
success: function (data) {
debugger;
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
debugger;
console.log(xmlHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
},
async: false
});
}
function getMappings() {
var grid = $("#Mappings").data("kendoGrid");
return grid._data;
}
Finally the last thing we have to do is change the controller-Action Parameter so that we have access to the instance of our ViewModel.
[HttpPost]
public virtual ActionResult Save(CrmProfileAddVM model)
{
using (DAKCrmImportContext db = new DAKCrmImportContext())
{
//crmProfile.CreatedAt = DateTime.Now;
//crmProfile.CreatedBy = System.Web.HttpContext.Current.User.Identity.Name.Split('\\')[1];
//try
//{
// db.CrmProfiles.Add(crmProfile);
// db.SaveChanges();
//}
//catch (DbEntityValidationException e)
//{
// var vals = e.EntityValidationErrors;
// throw;
//}
}
return RedirectToAction("Add", "Mapping", null);
}
Related
I created a view that was working wonderfully until I added some JQuery to support cascading drop downs. I believe in doing that, I broke the binding between the view and the model. I'm getting the error "No parameterless constructor defined for this object." when the form is submitted. The obvious solution would be to add a parameterless constructor, but I'm assuming that the postmodel will be null? Code Snippets below.
Thanks in Advance for your help.
View:
<script type="text/javascript">
$(document).ready(function () {
$("#ddlCategories").change(function () {
var iCategoryId = $(this).val();
$.getJSON(
"#Url.Content("~/Remote/SubCategoriesByCateogry")",
{ id: iCategoryId },
function (data) {
var select = ResetAndReturnSubCategoryDDL();
$.each(data, function (index, itemData) {
select.append($('<option/>', { value: itemData.Value, text: itemData.Text }));
});
});
});
function ResetAndReturnSubCategoryDDL() {
var select = $('#ddlSubCategory');
select.empty();
select.append($('<option/>', { value: '', text: "--Select SubCategory--" }));
return select;
}
});
...
<div class="editor-field">
#Html.DropDownList("iCategoryID", Model.Categories,"--Select Category--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlCategories"}})
#Html.ValidationMessage("iCategoryID")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubCategories, "SubCategory")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubCategories, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new { id = "ddlSubCategory" })
#Html.ValidationMessage("iSubCategoryID")
</div>
Controller:
[HttpPost]
public ActionResult Create(VendorCreateModel postModel)
{
VendorCreateEditPostValidator createValidator = new VendorCreateEditPostValidator(
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
m_unitOfWork.ProgramRepository,
new ModelStateValidationWrapper(ModelState));
if (ModelState.IsValid)
{
int categoryId = int.Parse(postModel.iCategoryID);
int subcategoryId = int.Parse(postModel.iSubCategoryID);
var programIds = postModel.AppliedPrograms.Select(ap => int.Parse(ap));
var programs = m_unitOfWork.ProgramRepository.GetPrograms(programIds);
Vendor vendor = postModel.Vendor;
vendor.Category = m_unitOfWork.CategoryRepository.GetCategory(categoryId);
vendor.SubCategory = m_unitOfWork.SubCategoryRepository.GetSubCategory(subcategoryId);
foreach (Program p in programs)
vendor.Programs.Add(p);
m_unitOfWork.VendorRepository.Add(vendor);
m_unitOfWork.SaveChanges();
return RedirectToAction("Index");
}
VendorCreateModel model = new VendorCreateModel(
postModel.Vendor,
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
User.Identity.Name,
m_unitOfWork.CategoryRepository,
m_unitOfWork.SubCategoryRepository,
m_unitOfWork.PermissionRepository);
return View(model);
}
RemoteController:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult SubCategoriesByCateogry(int id)
{
System.Diagnostics.Debug.WriteLine(id);
var SubCategories = db.SubCategories
.Where(v => v.iCategoryID == id)
.OrderBy(v => v.sDesc)
.ToList();
var modelData = SubCategories.Select(v => new SelectListItem()
{
Text = v.sDesc,
Value = v.iSubCategoryID.ToString()
});
return Json(modelData, JsonRequestBehavior.AllowGet);
}
VendorCreateModel:
public class VendorCreateModel
{
public VendorCreateModel()
{
}
public VendorCreateModel(
Vendor vendor,
string categoryId,
string subcategoryId,
IEnumerable<string> appliedPrograms,
string username,
ICategoryRepository categoryRepository,
ISubCategoryRepository subcategoryRepository,
IPermissionRepository permissionRepository)
{
UserHasProgramsValidator programValidator = new UserHasProgramsValidator(username, permissionRepository);
var availablePrograms = programValidator.AvailablePrograms;
HashSet<Category> applicableCategories = new HashSet<Category>();
foreach (var p in availablePrograms)
foreach (var c in categoryRepository.GetCategoriesByProgram(p.iProgramID))
applicableCategories.Add(c);
this.Vendor = vendor;
this.AppliedPrograms = appliedPrograms;
this.Categories = new SelectList(applicableCategories.OrderBy(x => x.sDesc).ToList(), "iCategoryID", "sDesc");
this.SubCategories = new SelectList(subcategoryRepository.GetAllSubCategories().OrderBy(x => x.sDesc).ToList(), "iSubCategoryID", "sDesc");
if (!string.IsNullOrEmpty(categoryId))
{
int temp;
if (!int.TryParse(categoryId, out temp))
throw new ApplicationException("Invalid Category Identifier.");
}
this.iCategoryID = categoryId;
this.iSubCategoryID = subcategoryId;
this.ProgramItems = availablePrograms
.Select(p => new SelectListItem()
{
Text = p.sDesc,
Value = p.iProgramID.ToString(),
Selected = (AppliedPrograms != null ? AppliedPrograms.Contains(p.iProgramID.ToString()) : false)
});
}
public Vendor Vendor { get; set; }
public SelectList Categories { get; set; }
public SelectList SubCategories { get; set; }
public string iCategoryID { get; set; }
public string iSubCategoryID { get; set; }
public IEnumerable<SelectListItem> ProgramItems { get; set; }
[AtLeastOneElementExists(ErrorMessage = "Please select at least one program.")]
public IEnumerable<string> AppliedPrograms { get; set; }
}
I correct the issue and wanted to share in case someone else was banging their head against their desk like Ihave been. Basically I changed the dropdownlistfor to reflect:
#Html.DropDownListFor(model => model.iSubCategoryID, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlSubCategory"},{"name","iSubCategoryID"}})
Assuming here the problem is in your VendorCreateModel, you either need to add a parameterless constructor or remove it, and create an instance in your action method and populate it by TryUpdateModel. Or parse the form using FormsCollection (not a fan).
You don't have the code for your viewmodel posted here but the basic assumption is that it will map.
I'm looking to display two Telerik Kendo grids with different information(models) in one view and I'm not sure how to do it.
Model:
using System.Collections.Generic;
namespace KendoUIMvcApplication1.Models
{
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Truck
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Vehicles
{
public IEnumerable<Car> CarCol { get; set; }
public IEnumerable<Truck> TruckCol { get; set; }
}
}
Controller (Creating some test objects):
using System.Collections.Generic;
using System.Web.Mvc;
using KendoUIMvcApplication1.Models;
namespace KendoUIMvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var carList = new List<Car>();
var truckList = new List<Truck>();
var vehCol = new Vehicles();
var car1 = new Car
{
Id = 1,
Name = "Passat"
};
var car2 = new Car
{
Id = 2,
Name = "Passat"
};
carList.Add(car1);
carList.Add(car2);
var truck1 = new Truck
{
Id = 1,
Name = "S10"
};
var truck2 = new Truck
{
Id = 1,
Name = "Blazer"
};
truckList.Add(truck1);
truckList.Add(truck2);
vehCol.CarCol = carList;
vehCol.TruckCol = truckList;
return View(vehCol);
}
}
}
View (Here I'm trying to display the two grids. The first one for cars and the second one for trucks):
#using KendoUIMvcApplication1.Models
#model IEnumerable<Vehicles>
#*// Car Grid*#
#(Html.Kendo().Grid(Model)
.Name("Grid")
.Columns(columns => {
columns.Bound(c => c.Id);
columns.Bound(c => c.Name);
})
)
<div></div>
#*// Truck Grid*#
#(Html.Kendo().Grid(Model))
.Name("Grid")
.Columns(columns => {
columns.Bound(t => t.Id);
columns.Bound(t => t.Name);
})
)
Your model should be of type Vehicles:
#model Vehicles
Then you can access the two collections:
#Html.Kendo().Grid(Model.CarCol)
...
and
#Html.Kendo().Grid(Model.TruckCol)
...
In Model:
public class DataViewModel
{
public IList<RowsCollection> Rows { get; set; }
public PaiementMethod PaiementMethod { get; set; }
}
public class RowsCollection
{
public string ID { get; set; }
public string Question { get; set; }
}
public enum PaiementMethod
{
Cash,
CreditCard,
}
In the Controller - Index ActionResult I have return my Model and render its into view page something like :
#model WebNexpo.Controllers.DataViewModel
#using (Html.BeginForm("Save", "page", FormMethod.Post, new { id = "saves"}))
{
foreach (var item in Model.Rows)
{
#Html.Label(item.Question)
<label for="paiement_cash">
Cash</label>
#Html.RadioButtonFor(m => m.PaiementMethod, "Cash", new { name = "paiement_cash" + #item.ID + "", id = "radioID" + #item.ID + "", Group = "Group" + #item.ID + "" })
<label for="paiement_cc">
Credit card</label>
#Html.RadioButtonFor(m => m.PaiementMethod, "CreditCard", new { name = "paiement_cc" + #item.ID + "", id = "radioname" + #item.ID + "", Group = "Group" + #item.ID + "" })
}
<input id="Saveser" type="submit" value="" class="button1" />
}
in submit form Action Event :
[HttpPost]
public ActionResult Save(DataViewModel model)
{
if (ModelState.IsValid)
{
//want to read all the label which selected rediobutton.
means suppose 4 question render on page and user have selected only 2
question of the answer.so How can accomplish here?
}
return RedirectToAction("Index", new {value = "123"});
}
There's some inconsistency here. You are rendering 2 radio buttons for each row so you probably want to reorganize your view model into:
public class DataViewModel
{
public IList<RowsCollection> Rows { get; set; }
}
public class RowsCollection
{
public string ID { get; set; }
public string Question { get; set; }
public PaiementMethod PaiementMethod { get; set; }
}
and then:
#model DataViewModel
#using (Html.BeginForm("Save", "page", FormMethod.Post, new { id = "saves"}))
{
for (var i = 0; i < Model.Rows.Count; i++)
{
<div>
#Html.HiddenFor(x => x.Rows[i].ID)
#Html.LabelFor(x => x.Rows[i].Question)
#Html.EditorFor(x => x.Rows[i].Question)
#Html.Label("payment_cash" + i, "Cash")
#Html.RadioButtonFor(m => m.Rows[i].PaiementMethod, "Cash", new { id = "payment_cash" + i })
#Html.Label("payment_cc" + i, "Credit card")
#Html.RadioButtonFor(m => m.Rows[i].PaiementMethod, "CreditCard", new { id = "payment_cc" + i })
</div>
}
<input id="Saveser" type="submit" value="" class="button1" />
}
and finally:
[HttpPost]
public ActionResult Save(DataViewModel model)
{
if (ModelState.IsValid)
{
// model.Rows[0].PaiementMethod will contain the selected payment method for the first question
// model.Rows[1].PaiementMethod will contain the selected payment method for the second question
// ...
}
return RedirectToAction("Index", new { value = "123" });
}
or if you want a single payment method you could keep your view model as is but then leave the radio buttons outside of the loop in your view. Like that:
#using (Html.BeginForm("Save", "page", FormMethod.Post, new { id = "saves" }))
{
for (var i = 0; i < Model.Rows.Count; i++)
{
<div>
#Html.HiddenFor(x => x.Rows[i].ID)
#Html.LabelFor(x => x.Rows[i].Question)
#Html.EditorFor(x => x.Rows[i].Question)
</div>
}
#Html.Label("payment_cash", "Cash")
#Html.RadioButtonFor(x => x.PaiementMethod, "Cash", new { id = "payment_cash" })
#Html.Label("payment_cc", "Credit card")
#Html.RadioButtonFor(x => x.PaiementMethod, "CreditCard", new { id = "payment_cc" })
<input id="Saveser" type="submit" value="" class="button1" />
}
I am new to MVC3
I am finding it difficult to create an dropdown.I have gone through all the other related questions but they all seem to be complex
I jus need to create a dropdown and insert the selected value in database
Here is what i have tried:
//Model class:
public int Id { get; set; }
public SelectList hobbiename { get; set; }
public string filelocation { get; set; }
public string hobbydetail { get; set; }
//Inside Controller
public ActionResult Create()
{
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
hobbiename = new SelectList(values, "Value", "Text")
};
return View();
}
//Inside view
<div class="editor-label">
#Html.LabelFor(model => model.hobbiename)
</div>
<div class="editor-field">
#Html.DropDownListFor( x => x.hobbiename, Model.hobbiename )
#Html.ValidationMessageFor(model => model.hobbiename)
</div>
I get an error:System.MissingMethodException: No parameterless constructor defined for this object
You are not passing any model to the view in your action. Also you should not use the same property as first and second argument of the DropDownListFor helper. The first argument that you pass as lambda expression corresponds to a scalar property on your view model that will hold the selected value and which will allow you to retrieve this value back when the form is submitted. The second argument is the collection.
So you could adapt a little bit your code:
Model:
public class Hobbies
{
[Required]
public string SelectedHobbyId { get; set; }
public IEnumerable<SelectListItem> AvailableHobbies { get; set; }
... some other properties that are irrelevant to the question
}
Controller:
public class HomeController: Controller
{
public ActionResult Create()
{
// obviously those values might come from a database or something
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
AvailableHobbies = values.Select(x => new SelectListItem
{
Value = x.Value,
Text = x.Text
});
};
return View(model);
}
[HttpPost]
public ActionResult Create(Hobbies hobbies)
{
// hobbies.SelectedHobbyId will contain the id of the element
// that was selected in the dropdown
...
}
}
View:
#model Hobbies
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedHobbyId)
#Html.DropDownListFor(x => x.SelectedHobbyId, Model.AvailableHobbies)
#Html.ValidationMessageFor(x => x.SelectedHobbyId)
<button type="submit">Create</button>
}
I would create them as
Model:
public class ViewModel
{
public int Id { get; set; }
public string HobbyName { get; set; }
public IEnumerable<SelectListItem> Hobbies {get;set; }
public string FileLocation { get; set; }
public string HobbyDetail { get; set; }
}
Action
public ActionResult Create()
{
var someDbObjects= new[]
{
new { Id = "1", Text = "Dancing" },
new { Id = "2", Text = "Painting" },
new { Id = "3", Text = "Singing" },
};
var model = new ViewModel
{
Hobbies = someDbObjects.Select(k => new SelectListItem{ Text = k, Value = k.Id })
};
return View(model);
}
View
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.HobbyName, Model.Hobbies )
#Html.ValidationMessageFor(model => model.HobbyName)
</div>
I want to have a dropdownlist in my view that displays a patient's ID, First Name, and Last Name. With the code below, it displays each patient's First Name. How can I pass all three properties into the viewbag and have them display in the dropdownlist?
Controller
public ActionResult Create()
{ViewBag.Patient_ID = new SelectList(db.Patients, "Patient_ID", "First_Name");
return View();
}
View
<div class="editor-field">
#Html.DropDownList("Patient_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Patient_ID)
</div>
Thanks.
Ok, I have edited my code as follows, and I receive the error "There is no ViewData item of type 'IEnumerable' that has the key 'SelectedPatientId'."
Controller
public ActionResult Create()
{
var model = new MyViewModel();
{
var Patients = db.Patients.ToList().Select(p => new SelectListItem
{
Value = p.Patient_ID.ToString(),
Text = string.Format("{0}-{1}-{2}", p.Patient_ID, p.First_Name, p.Last_Name)
});
var Prescribers = db.Prescribers.ToList().Select(p => new SelectListItem
{
Value = p.DEA_Number.ToString(),
Text = string.Format("{0}-{1}-{2}", p.DEA_Number, p.First_Name, p.Last_Name)
});
var Drugs = db.Drugs.ToList().Select(p => new SelectListItem
{
Value = p.NDC.ToString(),
Text = string.Format("{0}-{1}-{2}", p.NDC, p.Name, p.Price)
});
};
return View(model);
}
View Model
public class MyViewModel
{
[Required]
public int? SelectedPatientId { get; set; }
public IEnumerable<SelectListItem> Patients { get; set; }
[Required]
public int? SelectedPrescriber { get; set; }
public IEnumerable<SelectListItem> Prescribers { get; set; }
[Required]
public int? SelectedDrug { get; set; }
public IEnumerable<SelectListItem> Drugs { get; set; }
}
View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.DropDownListFor(
x => x.SelectedPatientId,
Model.Patients,
"-- Select patient ---"
)
#Html.ValidationMessageFor(x => x.SelectedPatientId)
<button type="submit">OK</button>
#Html.DropDownListFor(
x => x.SelectedPrescriber,
Model.Patients,
"-- Select prescriber ---"
)
#Html.ValidationMessageFor(x => x.SelectedPrescriber)
<button type="submit">OK</button>
}
I would recommend you not to use any ViewBag at all and define a view model:
public class MyViewModel
{
[Required]
public int? SelectedPatientId { get; set; }
public IEnumerable<SelectListItem> Patients { get; set; }
}
and then have your controller action fill and pass this view model to the view:
public ActionResult Create()
{
var model = new MyViewModel
{
Patients = db.Patients.ToList().Select(p => new SelectListItem
{
Value = p.Patient_ID.ToString(),
Text = string.Format("{0}-{1}-{2}", p.Patient_ID, p.First_Name, p.Last_Name)
});
};
return View(model);
}
and finally in your strongly typed view display the dropdown list:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedPatientId,
Model.Patients,
"-- Select patient ---"
)
#Html.ValidationMessageFor(x => x.SelectedPatientId)
<button type="submit">OK</button>
}
Easy way to accomplish that is just creating additional property on your model either by modifying model class or adding/modifying a partial class
[NotMapped]
public string DisplayFormat
{
get
{
return string.Format("{0}-{1}-{2}", Patient_ID, First_Name, Last_Name);
}
}