I'm having some trouble getting my ViewModel to return a non-null object in the base model property in my controller's Create action postback. I currently have another page that is doing the exact same operations on another model that has almost the same properties and that form is working perfectly, so I feel like I'm missing something basic, though I can't place what is wrong.
Here's my ViewModel with my Base class:
public class SystemFailureActionViewModel
{
/// <summary>
/// View model class for adding and modifying SystemFailureActions
/// </summary>
public SystemFailureAction action { get; set; }
//properties
public int TypeID { get; set; }
public string TypeDescription { get; set; }
public bool Assigned { get; set; }
public SystemFailureActionViewModel() { }
public SystemFailureActionViewModel(SystemFailureAction action)
{
this.action = action;
}
//Collections for views
public IEnumerable<SelectListItem> EditSystemFailiureTypesList { get { return ModelListProvider.FilteredSystemFailureTypeList; } }
public IEnumerable<SelectListItem> DetailsSystemFailiureTypesList { get { return ModelListProvider.FullSystemFailureTypeList; } }
}
[MetadataType(typeof(SystemFailureActionMetadata))]
public partial class SystemFailureAction
{
private class SystemFailureActionMetadata
{
/// <summary>
/// ID for this failure action
/// </summary>
public int ID { get; set; }
/// <summary>
/// Action Description
/// </summary>
[Required]
[StringLength(200)]
[DisplayName("Action Description")]
public string Description { get; set; }
}
}
Here's my Controller Add and Add Postback methods:
public ActionResult Add()
{
SystemFailureAction action = new SystemFailureAction();
action.Description = "";
populateSystemFailureActionData(action);
return PartialView("Form", new SystemFailureActionViewModel(action));
}
[HttpPost]
public ActionResult Add(SystemFailureActionViewModel viewModel, string[] selectedTypes, FormCollection collection)
{
SystemFailureAction newAction = viewModel.action;
if (!ModelState.IsValid)
{
populateSystemFailureActionData(newAction);
return PartialView("Form", new SystemFailureActionViewModel(newAction));
}
try
{
//Insert the new failure action type
context.SystemFailureActions.InsertOnSubmit(newAction);
context.SubmitChanges();
//Insert the failure type mappings
updateSystemFailureAssociationData(newAction, selectedTypes);
context.SubmitChanges();
//Return the new data
populateSystemFailureActionData(newAction);
return PartialView("Done", new SystemFailureActionViewModel(newAction));
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
populateSystemFailureActionData(newAction);
return PartialView("Form", new SystemFailureActionViewModel(newAction));
}
}
And finally here is my form, it is being loaded into a Jquery dialog and the postback is being done via Ajax.
#using (Ajax.BeginForm(new AjaxOptions
{
HttpMethod = "Post",
UpdateTargetId = "formDialog",
InsertionMode = InsertionMode.Replace,
OnSuccess = "onDialogDone()"
}))
{
#Html.ValidationSummary(true)
<div>
<div class="editor-label">
#Html.LabelFor(model => model.action.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.action.Description)
</div>
</div>
<div class="categoryFieldSet">
<fieldset>
<legend>Mechanical System Failure Categories</legend>
<div class="editor-field">
<table>
<tr>
#{
int count = 0;
List<ManageMAT.ViewModels.SystemFailureActionViewModel> types = ViewBag.Types;
foreach (var type in types)
{
if (count++ % 3 == 0)
{
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
id="selectedTypes + #type.TypeID"
name="selectedTypes"
value="#type.TypeID"
#(Html.Raw(type.Assigned ? "checked=\"checked\"" : "")) />
<label for="selectedTypes + #type.TypeID">#type.TypeDescription</label>
#:</td>
}
#:</tr>
}
</table>
</div>
</fieldset>
</div>
}
If you're wondering what the ViewBag.Types logic is in the form it is related to this question I asked earlier.
Edit:
I checked the ModelState error and the exception I'm getting is
"The parameter conversion from type 'System.String' to type Models.SystemFailureAction' failed because no type converter can convert between these types."
I also removed the logic that adds the Failure types and I'm still receiving the same issue. So it appears the problem is coming from mapping the Viewmodel.action.Description to the action.
The problem is the following property on your SystemFailureActionViewModel:
public SystemFailureAction action { get; set; }
In ASP.NET MVC action and controller are kinda reserved words. They are part of every route. And if you have a property called this way it conflicts with the string value which is pointing to the action name and which obviously cannot be bound to a complex SystemFailureAction type.
So to fix the problem simply rename this property on your view model:
public SystemFailureAction FailureAction { get; set; }
Related
This is my method on controller "sale"
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Models.account account)
{
Models.sale creaventa = new Models.sale();
//creaventa.account = cliente;
creaventa.createdon = DateTime.Now;
creaventa.idaccount = account.id;
creaventa.modifiedon = DateTime.Now;
creaventa.status = 0;
context.sales.Add(creaventa);
context.SaveChanges();
// return "venta creada";
return View();
}
and this is the partial view
#model List<modal3.Models.account>
#{
ViewBag.Title = "Create";
}
<select class="form-control" id="control1">
#{
foreach (var cliente in Model)
{
<option value="#cliente.id"> #cliente.name</option>
}
}
</select>
#*#using (Html.BeginForm("create", "sale", FormMethod.Post, new {id="my-form" }))
{
#Html.AntiForgeryToken()
<button type="submit" class="btn btn-default" value="Create" id="btncrear">
Iniciar Venta
</button>
}*#
#using (
Ajax.BeginForm("create","sale",new AjaxOptions()
{
HttpMethod ="Post",
InsertionMode = InsertionMode.Replace,
})
)
{
#Html.AntiForgeryToken()
<button type="submit" class="btn btn-default" value="Create" id="btncrear">
Iniciar Venta
</button>
}
This does enter to method with this does not send the model.
Then:
How to send a model?
How to send a lot of objects?
this is my model
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace modal3.Models
{
using System;
using System.Collections.Generic;
public partial class sale
{
public sale()
{
this.saledetails = new HashSet<saledetail>();
}
public int id { get; set; }
public Nullable<System.DateTime> createdon { get; set; }
public Nullable<System.DateTime> modifiedon { get; set; }
public Nullable<int> status { get; set; }
public Nullable<int> idaccount { get; set; }
public virtual account account { get; set; }
public virtual ICollection<saledetail> saledetails { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace modal3.Models
{
[MetadataType (typeof (sale_validation ))]
public partial class sale
{
}
public class sale_validation
{
//2015-06-17 22:07:26.353 2015-06-17 22:07:26.353 1 1
[Display (Name="")]
[HiddenInput (DisplayValue =false )]
public Nullable<System.DateTime> createdon { get; set; }
[Display(Name = "")]
[HiddenInput(DisplayValue = false)]
public Nullable<System.DateTime> modifiedon { get; set; }
[Display(Name = "")]
[HiddenInput(DisplayValue = false)]
public Nullable<int> status { get; set; }
public Nullable<int> idaccount { get; set; }
}
}
To make you understand how AJAX FORM works, I created below code -
Lets say our model -
public class Sale
{
public string SaleOwner { get; set; }
public virtual Account Account { get; set; }
}
public class Account
{
public string Name { get; set; }
}
I created two controller actions -
public ActionResult adatas()
{
return View();
}
[HttpPost]
public JsonResult Create(Sale s)
{
return Json("true");
}
The first controller action return following view -
#model WebApplication1.Controllers.Sale
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
#using (Ajax.BeginForm("Create", "Sale", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "done"
}))
{
#Html.TextBoxFor(m => m.SaleOwner)
#Html.TextBoxFor(m => m.Account.Name)
<input type="submit" value="click" />
}
<div id="done">
</div>
View Renders as follows -
Once we click on button, with breakpoints in the code -
Once AJAX POST happens, output would be -
Hi im using RenderAction helper and during rendering i have this error:
Error executing child request for
handler
'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
Controller code:
public ActionResult GetSearcher()
{
SearcherLines sl = new SearcherLines()
{
RegionList = db.Region.ToList(),
CategoryList = db.Category.ToList(),
SearchValue = null
};
return PartialView(sl);
}
ViewModel
public class SearcherLines
{
public List<Region> RegionList { get; set; }
public List<Category> CategoryList { get; set; }
public string SearchValue { get; set; }
}
partialView
#model IEnumerable<MasterProject.JobForYou.v3.Models.ViewModels.SearcherLines>
#foreach (var i in Model)
{
<p>#i.CategoryList</p><br />
}
Please help.
Try setting OutputCache attribute on controller action
I am trying to create an Item that has an ItemType coming from another table. I am unable to get back the actual Type object from the Create page. This is the code I have tried:
Models:
public class ItemType {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Item> Item{ get; set; }
}
public class Item {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ItemType ItemType { get; set; }
}
In the ItemController, this is my create code:
public ActionResult Create() {
var itemTypeRepo = new ItemTypeRepository(context);
ViewBag.ItemTypes = new SelectList(itemTypeRepo.GetAll(), "ID", "Name");
return View();
}
[HttpPost]
public ActionResult Create(Item item) {
if (ModelState.IsValid) {
context.Items.Add(item);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
In my Create.cshtml view I have tried:
<div class="editor-field">
#Html.DropDownList("ItemType", String.Empty)
#Html.ValidationMessageFor(model => model.ItemType)
</div>
This returns no value at all and throws an error "The value 'X' is invalid." Where X is the ID of the ItemType I selected.
And
<div class="editor-field">
#Html.DropDownListFor(x => x.ItemType.Id, (SelectList)ViewBag.ItemType)
#Html.ValidationMessageFor(model => model.ItemType)
</div>
This creates a stub ItemType object with the correct ID but does not insert it into the database since the object is not fully loaded. If I look at ModelState object, I find that there is an error that the Name field is missing from ItemType object.
I also attempted to solve the problem using the second .cshtml code and adding this code:
public ActionResult Create(Item item) {
item.ItemType = context.ItemTypes.Find(item.ItemType.Id);
if (ModelState.IsValid)
This does not change the value of ModelState.IsValid from false even through it should.
What do I need to do to get this to work?
You should add a property ItemTypeId to your Item entity so that it acts as a foreign key.
public class Item
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int ItemTypeId { get; set; }
[ForeignKey("ItemTypeId")]
public virtual ItemType ItemType { get; set; }
}
You can then use that property for the dropdownlist:
<div class="editor-field">
#Html.DropDownListFor(x => x.ItemTypeId, (SelectList)ViewBag.ItemType)
#Html.ValidationMessageFor(model => model.ItemType)
</div>
How do I use MVC3 editor templates for lists with add and delete?
I have an object:
public class Policy
{
public List<PolicyLine> PolicyLines = new List<PolicyLine>();
}
public class PolicyLine
{
public PolicyLine(bool isPositive, string policyText)
{
IsPositive = isPositive;
PolicyText = policyText;
}
public bool IsPositive { get; set; }
public string PolicyText { get; set; }
}
I have an editorTemplate: in Views\Shared\EditorTemplates\Policy.cshtml and Views\Shared\EditorTemplates\PolicyLine.cshmtml and I'm wondering how to enable users to add and delete PolicyLines from the Policy?
For the DELETE, just add the following line to the PolicyLine.cshtml and add a Delete Action to your Controller to perform the delete.
#Html.ActionLink("Delete", "Delete", new { id = #Model.PolicyID })
The ADD is a bit trickier, you could add button to you Policy.cshtml and then call some javascript to insert some html on the fly.
OR
You could have the button display a new page to capture the new policyline and then return to the original page with the new line added.
I got this to work for me:
Here is my Views/Policy/Index.cshtml
#using (Html.BeginForm("Submit", "Policy")) {
<fieldset>
#Html.EditorForModel()
</fieldset>
}
Here is my Views/Shared/EditorTemplates/Policy.cshtml
#model Policy
<br />
<label for="IsPositive">Is positive?</label>
#Html.CheckBox("IsPositive")
<input type="text" name="PolicyText" />
<input type="submit" value="Add to Policy" title="SubmitFromReferalPolicy" />
#Html.EditorFor(a => a.PolicyLines)
Here is my Views/Shared/EditorTemplates/PolicyLine.cshtml
#model PolicyLine
<br />
#this.Model.ToString()
#Html.ActionLink("Delete", "DeleteLine/" + Model.Identifier.ToString())
Here is my Policy.cs
public class Policy
{
public string Id { get; set; }
public List<PolicyLine> PolicyLines = new List<PolicyLine>();
public override string ToString()
{
return PolicyFormatter.FormatPolicy(this);
}
}
Here is my PolicyLine.cs
public class PolicyLine
{
public bool IsPositive { get; set; }
public string PolicyText { get; set; }
public Guid Identifier { get; set; }
public override string ToString()
{
return PolicyFormatter.FormatPolicyLine(this);
}
}
Here is my add method from PolicyController.cs
[HttpPost]
public ActionResult Submit(PolicyLine submitted)
{
Policy saveMe = Policy.GetPolicyFromUserName(UserName);
submitted.Identifier = Guid.NewGuid();
saveMe.PolicyLines.Add(submitted);
Store.Write(saveMe);
return RedirectToAction("Index");
}
Here is my delete method from PolicyController.cs
public ActionResult DeleteLine(Guid identifier)
{
Policy saveMe = Policy.GetPolicyFromUserName(UserName);
PolicyLine removeMe = saveMe.PolicyLines.Find(p => p.Identifier == identifier);
saveMe.PolicyLines.Remove(removeMe);
Store.Write(saveMe);
return RedirectToAction("Index");
}
i have a model SiteMapModel that have a object VirtualFolderModel inside him.
public class SiteMapModel
{
public SiteMapModel(DataRow data)
{
SMF_ID = Convert.ToInt32(data["SMF_ID"]);
SMF_VF_ID = Convert.ToInt32(data["SMF_VF_ID"]);
VirtualFolder = new VirtualFolderModel(data);
}
public VirtualFolderModel VirtualFolder;
public int SMF_ID { get; set; }
public int SMF_VF_ID { get; set; }
}
public class VirtualFolderModel
{
public VirtualFolderModel(DataRow data)
{
VF_ID = Convert.ToInt32(data["VF_ID"]);
}
public int VF_ID { get; set; }
}
in my controller i pass the model to a view.
public ActionResult Edit(int id)
{
SiteMapData smd = new SiteMapData();
SiteMapModel smm = new SiteMapModel(smd.GetFolderData((int)id, 15));
return View(smm);
}
how to use it in my view?
<div>
<span class="editor-label">
#Html.Label("Title")
</span>
#Html.TextBox("SMF_Name")
#Html.ValidationMessage("SMF_Name")
<span class="editor-label">
#Html.Label("VF_ID")
</span>
#Html.TextBox("VF_ID")
#Html.ValidationMessage("VF_ID")
<input type="submit" value="Save" />
</div>
the #Html.TextBox("VF_ID") don't work
At the top of your view add this:
#ModelType SitemapModel
Edit: For C# please use:
#model SitemapModel
Doing that will simply tell your view what kind of model is given at runtime. In this case, it's an object of type SitemapModel.
In your view you can reference to it to model.SMF_ID