In an ASP.net MVC 5 application, I'm using Summernote.js to display an HTML text editor for users to input a message. My model includes the necessary attribute:
[AllowHtml]
[Display(Name = "Welcome Message")]
public string message { get; set; }
My controller binds all the fields to an object and writes them to the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,name,surveytype,industryId,start,end,message,resultsdate,resultsfile,resultsfile2")] Survey survey, HttpPostedFileBase bentemplate, HttpPostedFileBase comptemplate)
{
if (ModelState.IsValid)
{
if(bentemplate != null && bentemplate.ContentLength > 0)
{
var filename = Path.GetFileName(bentemplate.FileName);
var fullpath = Path.Combine(Server.MapPath("~/templates"), filename);
bentemplate.SaveAs(fullpath);
survey.bentemplateaddress = fullpath;
}
if (comptemplate != null && comptemplate.ContentLength > 0)
{
var filename = Path.GetFileName(comptemplate.FileName);
var fullpath = Path.Combine(Server.MapPath("~/templates"), filename);
bentemplate.SaveAs(fullpath);
survey.comptemplateaddress = fullpath;
}
db.surveys.Add(survey);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(survey);
}
Relevant view code (the textarea-editor class make it appear as a Summernote editor):
#Html.LabelFor(model => model.message, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.message, new { htmlAttributes = new { #class = "form-control input-lg textarea-editor" } })
#Html.ValidationMessageFor(model => model.message, "", new { #class = "text-danger" })
Unfortunately, everything is being written except the welcome message.
Any suggestions on what is causing this?
Thanks!
Related
I have been trying to find a solution to a situation that I'm busy designing, however I have not managed to get to it.
Imagine having the following model
public enum InputType
{
TextInput,
LookupInput
}
public struct AdditionalProperty
{
public string Key {get;set;}
public string Value {get;set;}
public InputType Type {get;set;}
}
public class Person
{
public string FirstName {get;set;}
public List<AdditionalProperty> AdditionalProperties {get;set;}
}
Then, having the following controller
public class HomeController
{
public ActionResult Index()
{
var model = new Person { FirstName = "MyName" };
model.AdditionalProperties = new List<AdditionalProperty>();
var listItem = new AdditionalProperty
{
Key = "Surname",
Value = "MySurname"
};
model.AdditionalProperties.Add(listItem);
return View(model)
}
}
What I'm looking for is the Razor view code on how to "dynamically" create the properties with the correct input type, bound to something in order for me to be able to still use the model when the form gets posted back to the controller for a Save function.
So the property that is known, would be something like this:
<div class="form-group">
<div class="form-row">
<div class="col-md-6">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label" })
<div>
#Html.TextBoxFor(model => model.FirstName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
The Idea would then be to have the following. Obviously the below isn't sufficient, and this is where I need the help.
I would like to show the additional properties, one below the other, each on a separate line (using bootstrap row) based on the property.InputType
#foreach (var property in Model.Properties)
{
#Html.LabelFor(model => property.Key, new { #class = "control-label" })
<div>
#if (property.InputType == TextInput)
{
#Html.TextBoxFor(model => property.Value, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
}
#Html.ValidationMessageFor(model => property.Key, "", new { #class = "text-danger" })
</div>
}
Thus, I would like to see my view as:
| <label> | <input>
Known Property | FirstName | MyFirstName
Unknown Property | Surname | MySurname
In terms of completeness, I am posting the following answer.
I am going to post the Model, View (Index & EditorTemplates) & Controller to show the complete working solution that I used to test the answer that was given to me.
My Model class for this test
Person.cs
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<AdditionalProperty> AdditionalProperties { get; set; }
}
AdditionalProperty.cs
public struct AdditionalProperty
{
public string Key { get; set; }
public object Value { get; set; }
public DateTime? DateValue
{
get
{
DateTime dateValue;
if (DateTime.TryParse(Value?.ToString(), out dateValue))
{
return dateValue;
}
return null;
}
set => Value = value;
}
public InputType InputType { get; set; }
public List<SelectListItem> ValueLookupItems { get; set; }
}
The reason I have a separate DateValue property here is to assist the browser when doing DateTime binding otherwise the DateTimePicker doesn't show.
I used an enum to determine what type of input type this specific property should make use of.
InputType.cs
public enum InputType
{
TextBox,
DropdownBox,
TextArea,
DateSelection,
}
In order to keep the views as simple as possible, Stephen provided me with a sample for the Index View as well as an EditorTemplate for the AdditionalProperty object. The EditorTemplate is used for separation of concerns and to ensure that all the logic behind what input type is being used is in one place.
I have found that the DateTime property doesn't work well, so an additional EditorTemplate was required. I got this from this post.
DateTime.cshtml
Note: Location of template -> /Views/Shared/EditorTemplates
#model DateTime
#{
IDictionary<string, object> htmlAttributes;
object objAttributes;
if (ViewData.TryGetValue("htmlAttributes", out objAttributes))
{
htmlAttributes = objAttributes as IDictionary<string, object> ?? HtmlHelper.AnonymousObjectToHtmlAttributes(objAttributes);
}
else
{
htmlAttributes = new RouteValueDictionary();
}
htmlAttributes.Add("type", "date");
String format = (Request.UserAgent != null && Request.UserAgent.Contains("Chrome")) ? "{0:yyyy-MM-dd}" : "{0:d}";
#Html.TextBox("", Model, format, htmlAttributes)
}
AdditionalProperty.cshtml
Note: Location of template -> /Views/Shared/EditorTemplates
Note: The location of my AdditionalProperty formed part of the DynamicViewExample.Models namespace
#model DynamicViewExample.Models.AdditionalProperty
<div>
#Html.HiddenFor(m => m.Key)
#Html.LabelFor(m => m.Key, Model.Key, new {#class = "control-label"})
#if (Model.InputType == DynamicViewExample.Models.InputType.TextBox)
{
#Html.TextBoxFor(m => m.Value, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.TextArea)
{
#Html.TextAreaFor(m => m.Value, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.DropdownBox)
{
#Html.DropDownListFor(m => m.Value, Model.ValueLookupItems, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.DateSelection)
{
#Html.EditorFor(m => m.DateValue, new {#class = "form-control"})
}
else
{
#Html.HiddenFor(m => m.Value) // we need this just in case
}
</div
This would be how the Index.cshtml file would look
#model DynamicViewExample.Models.Person
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
<div class="row">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label" })
#Html.TextBoxFor(model => model.FirstName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
<div class="row">
#Html.LabelFor(model => model.LastName, new { #class = "control-label" })
#Html.TextBoxFor(model => model.LastName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
<div class="row">
#Html.EditorFor(m => m.AdditionalProperties, new { htmlAttributes = new { #class = "form-control"}})
</div>
<input type="submit" class="btn btn-primary" />
}
And then finally, the HomeController.cs file contains a Get and Post that allows the ability to manipulate the data as you please. What is missing here is the "dynamic" way of populating the model, but that will naturally happen once a DB has been introduced into the mix.
[HttpGet]
public ActionResult Index()
{
var model = new Person
{
FirstName = "Gawie",
LastName = "Schneider",
AdditionalProperties = new List<AdditionalProperty>
{
new AdditionalProperty {Key = "Identification Number", Value = "1234567890123456", InputType = InputType.TextBox},
new AdditionalProperty {Key = "Date Of Birth", Value = DateTime.Today, InputType = InputType.DateSelection},
new AdditionalProperty {Key = "Age", Value = "31", InputType = InputType.TextBox},
new AdditionalProperty {Key = "Gender", Value = "Male", InputType = InputType.DropdownBox,
ValueLookupItems = new List<SelectListItem>
{
new SelectListItem{Text = "Male", Value = "Male"},
new SelectListItem{Text = "Female", Value = "Female"}
}},
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(Person model)
{
//Do some stuff here with the model like writing it to a DB perhaps
return RedirectToAction("Index");
}
So if I would have to sum up what I was trying to do here.
The goal I wanted to achieve was to be able to make use of Strongly Typed / Known Properties in conjunction with Dynamic / Unknown Properties to create a system that would allow the user to create new inputs on the fly without the need for a developer to be involved.
I honestly hope that this might help someone else as well some day.
Enjoy the coding experience
Gawie
I want to Check whether the master type already exists or not using remote validation.
In my case the remote validation method is not firing.Can anyone help me?
Model
[Key, Column(Order = 1)]
[StringLength(200)]
[Display(Name = "MasterType")]
[Remote("IsNameAvailable", "MasterSetUps", ErrorMessage = "Master type already exists ")]
public string MasterType { get; set; }
Validation Method
[AllowAnonymous]
public JsonResult IsNameAvailable(string MasterType)
{
bool result = true;
if (s_mode == "ADD")
{
return Json(!db.MasterSetUps.Any(a => a.MasterType == MasterType), JsonRequestBehavior.AllowGet);
}
else if (s_mode == "EDIT" & MasterType != s_Master_Type_name)
{
return Json(!db.MasterSetUps.Any(a => a.MasterType == MasterType), JsonRequestBehavior.AllowGet);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
View
<div class="form-group">
#Html.LabelFor(model => model.MasterType, htmlAttributes: new { #class = "control-label col-sm-2" })
<div class="col-sm-10">
#Html.DropDownList("MasterType", null, htmlAttributes: new { #class = "form-controls" })
#Html.ValidationMessageFor(model => model.MasterType, "", new { #class = "text-danger" })
</div>
</div>
GetMethod
public ActionResult Create()
{
s_mode = "ADD";
ViewBag.MasterType = new SelectList(db.Masters, "MasterType", "MasterType");
return View();
}
Your use of DropDownList(...) means that your not generating the necessary data-val-* attributes for validation. The method is using your ViewBag property for binding (not your model property) and their are no validation attributes associated with ViewBag.
Change the code in the GET method to
ViewBag.MasterTypeList = new SelectList (.....
and the view code to
#Html.DropDownListFor (m => m.MasterType, (SelectList)ViewBag.MasterTypeList, new { ... })
Note that the name of the property your binding to cannot be the same as the SelectList.
New to ASP.NET MVC and I've come across this before, however I feel I've gone about fixing it in an incorrect way. I was hoping someone could point me in the direction of the correct way of doing it.
I have a page which the user selects a dropdown value and clicks next. Now, if they don't select an item and select the default value ("Select..."), there is a validation error. The controller seems to lose information about the dropdownlist even though the model is returned to the view on a "postback." So, say I don't basically repeat code in my [HTTPPost] from my [HTTPGet]. I get the error:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'SheetIndex'.
on
#Html.DropDownListFor(model => model.SheetIndex, Model.SheetsDropdown, "Select...", new { #class = "form-control" })
So unless I repeat code, I get that error when no selection is made. What am I doing wrong?
ViewModel:
public class SelectSheetViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
public string SheetIndex { get; set; }
public string SheetName { get; set; }
public int? ChainId { get; set; }
public int? SheetId { get; set; }
public int? FileId { get; set; }
public IEnumerable<SelectListItem> SheetsDropdown { get; set; }
public HeaderViewModel Header { get; set; }
}
Controller:
[HttpGet]
public ActionResult SelectSheet(int? chainId, int? sheetId, int? fileId)
{
if (sheetId == null || fileId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var fileDetails = db.FileDetails.Find(fileId);
SelectSheetViewModel selectSheet = new SelectSheetViewModel()
{
Id = fileDetails.FileId,
Name = fileDetails.Name,
ChainId = chainId,
SheetId = sheetId,
FileId = fileId
};
string fileName = fileDetails.UniqueName + fileDetails.Extension;
string relativeFileLocation = "~/uploads/" + fileName;
string absoluteFileLocation = HttpContext.Server.MapPath(relativeFileLocation);
if (System.IO.File.Exists(absoluteFileLocation))
{
DSDBuilder builder = new DSDBuilder();
selectSheet.SheetsDropdown = builder.GetSheets(absoluteFileLocation); // Where I get my selectlist
}
else
{
ModelState.AddModelError("SheetDropdown", "Excel workbook does not exist.");
}
selectSheet.Header = BuildHeaderViewModel(chainId, sheetId);
return View(selectSheet);
}
[HttpPost]
public ActionResult SelectSheet(SelectSheetViewModel selectSheet)
{
if (ModelState.IsValid)
{
FileDetail fileDetails = db.FileDetails.Find(selectSheet.FileId);
string[] sheetIndexAndName = selectSheet.SheetIndex.Split(':');
fileDetails.SheetIndex = Convert.ToInt32(sheetIndexAndName[0]);
fileDetails.SheetName = sheetIndexAndName[1];
db.SaveChanges();
return RedirectToAction("Build", "Sheets", new
{
ChainId = selectSheet.ChainId,
SheetId = selectSheet.SheetId,
FileId = selectSheet.FileId
});
}
// Probably not a good method vvv
var fileDetailsPostBack = db.FileDetails.Find(selectSheet.FileId);
string fileName = fileDetailsPostBack.UniqueName + fileDetailsPostBack.Extension;
string relativeFileLocation = "~/uploads/" + fileName;
string absoluteFileLocation = HttpContext.Server.MapPath(relativeFileLocation);
if (System.IO.File.Exists(absoluteFileLocation))
{
DSDBuilder builder = new DSDBuilder();
selectSheet.SheetsDropdown = builder.GetSheets(absoluteFileLocation);
}
else
{
ModelState.AddModelError("SheetDropdown", "Excel workbook does not exist.");
}
// Probably not a good method ^^^
return View(selectSheet);
}
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ChainId)
#Html.HiddenFor(model => model.SheetId)
#Html.HiddenFor(model => model.FileId)
#Html.HiddenFor(model => model.Header.ChainName)
#Html.HiddenFor(model => model.Header.SheetName)
#Html.HiddenFor(model => model.Header.SheetDescription)
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.SheetIndex, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SheetIndex, Model.SheetsDropdown, "Select...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.SheetIndex, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Next" class="btn btn-default" />
</div>
</div>
</div>
}
I also would not like to repeat that code because it goes to a function which uses Excel.Interop and is kind of resource heavy. Let me know if anyone has a better solution. I'm always trying to improve my code and do things the "correct" way.
I am using boilerplate CRUD methods, which include BIND operations on the methods that catch the form submissions. I have discovered that if I have my fields as nullable both in the model as well as in the DB, and I do not include those fields in a CRUD operation (also no presence in BIND), these fields end up null when before they were filled. If I flag these fields as not nullable in the DB, I cannot complete the CRUD operation without including these fields in hidden form fields because they are not nullable.
How do I make a CRUD operation ignore these fields without adding them as hidden fields in the forms?? As in, do not null them, do not change their data.
For example, if I have my POST method as such:
public async Task<ActionResult> Edit([Bind(Include = "CompanyId,CompanyName,CompanyAddress,CompanyCity")] Company company) {
And there is a field both in the model as well as in the DB such as CompanyCity, if it is nullable in model and db it gets nulled with the update. If it is not nullable in the model and db, the update fails because the field is not nullable but the update wants to null it because it didn't exist in the bind.
I am also using only the base models, such as Company, for this example. However when I try to make another base model, such as EditCompanyViewModel, I am unable to pull data out of the database to put into that view model. The entire await command gets flagged as being not of the correct model/type.
Essentially, I need to know how to edit only part of a table, without messing/mucking/deleting the rest of the table entries and without creating a metric arseload of hidden form fields that exist purely to hold the data I don't want to edit.
I have a conceptual gap here, and I am metaphorically chasing my tail. I can't seem to bridge the gap to a solution.
EDIT:
My modified view model:
public class EditMarketingViewModel {
[Key]
public Guid CompanyId { get; set; }
[DisplayName("How did you hear of us")]
public Guid? HowHeardId { get; set; }
[DisplayName("eNewsletter")]
public bool eNewsletter { get; set; }
[DisplayName("Event Code")]
public Guid? EventCodeId { get; set; }
[DisplayName("Notes")]
public string MarketingNotes { get; set; }
#region Essentials
[HiddenInput, Timestamp, ConcurrencyCheck]
public byte[] RowVersion { get; set; }
[HiddenInput]
public DateTime Modified { get; set; }
[HiddenInput]
public string TouchedBy { get; set; }
#endregion
}
My view:
#model CCS.Models.EditMarketingViewModel
#{
ViewBag.Title = "Edit Marketing Info.";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#ViewBag.Title</h2>
#using(Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<fieldset>
#Html.HiddenFor(model => model.CompanyId)
#Html.HiddenFor(model => model.RowVersion)
#Html.LabelFor(model => model.HowHeardId, htmlAttributes: new { #class = "control-label" })#Html.DropDownList("HowHeardId", null, " « ‹ Select a How Heard Type › » ", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.HowHeardId, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.eNewsletter, htmlAttributes: new { #class = "control-label" })#Html.EditorFor(model => model.eNewsletter, new { htmlAttributes = new { #data_on_text = "Yes", #data_off_text = "No" } })
#Html.ValidationMessageFor(model => model.eNewsletter, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.EventCodeId, htmlAttributes: new { #class = "control-label" })#Html.DropDownList("EventCodeId", null, " « ‹ Select an Event Code › » ", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.EventCodeId, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.MarketingNotes, htmlAttributes: new { #class = "control-label" })#Html.EditorFor(model => model.MarketingNotes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.MarketingNotes, "", new { #class = "text-danger" })
<input type="submit" value="Save" class="btn btn-default" />
</fieldset>
}
<p>[ #Html.ActionLink("Back to List", "Index", "Company") ]</p>
Now how do I modify my controller to work with it:
// GET: Company/EditMarketing
public async Task<ActionResult> EditMarketing() {
var id = new Guid(User.GetClaimValue("CWD-Company"));
Company company = await db.Company.FindAsync(id);
if(company == null) {
return HttpNotFound();
}
ViewBag.HowHeardId = new SelectList(db.HowHeard.Where(x => x.Active == true).OrderBy(x => x.SortOrder), "HowHeardId", "HowHeardType", company.HowHeardId);
ViewBag.EventCodeId = new SelectList(db.EventCode.Where(x => x.Active == true).OrderBy(x => x.EventCodeDate), "EventCodeId", "EventCodeName", company.EventCodeId);
return View(company);
}
EDIT 2:
My POST:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditMarketing([Bind(Include = "CompanyId,HowHeardId,eNewsletter,EventCodeId,MarketingNotes,RowVersion")] Company company) {
try {
if(ModelState.IsValid) {
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
company.Modified = DateTime.UtcNow;
company.TouchedBy = User.Identity.GetFullNameLF();
db.Entry(company).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index", "Company");
}
} catch(DbUpdateConcurrencyException ex) {
var entry = ex.Entries.Single();
var companyValues = (Company)entry.Entity;
var databaseValues = (Company)entry.GetDatabaseValues().ToObject();
if(databaseValues.MarketingNotes != companyValues.MarketingNotes) { ModelState.AddModelError("MarketingNotes", "Current Value: " + databaseValues.MarketingNotes); }
if(databaseValues.eNewsletter != companyValues.eNewsletter) { ModelState.AddModelError("eNewsletter", "Current Value: " + databaseValues.eNewsletter); }
if(databaseValues.HowHeardId != companyValues.HowHeardId) { ModelState.AddModelError("HowHeardId", "Current Value: " + db.HowHeard.Find(databaseValues.HowHeardId).HowHeardType); }
if(databaseValues.EventCodeId != companyValues.EventCodeId) { ModelState.AddModelError("EventCodeId", "Current Value: " + db.EventCode.Find(databaseValues.EventCodeId).EventCodeName); }
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
company.RowVersion = databaseValues.RowVersion;
} catch(DataException dex) {
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists please inform your Manager, who will inform the developers." + dex);
}
ViewBag.HowHeardId = new SelectList(db.HowHeard.Where(x => x.Active == true).OrderBy(x => x.SortOrder), "HowHeardId", "HowHeardType", company.HowHeardId);
ViewBag.EventCodeId = new SelectList(db.EventCode.Where(x => x.Active == true).OrderBy(x => x.EventCodeDate), "EventCodeId", "EventCodeName", company.EventCodeId);
return View(company);
}
Please note that I am making use of concurrency to avoid data collisions. Hence the RowVersion column.
As I understand your code EditMarketingViewModel is used to update a company record. Pass your view model as parameter to your post action result. You would want to load the company record first before updating it like so. This approach makes your record retain property values which are not needed to be updated.
[HttpPost]
public async Task<ActionResult> Edit(EditMarketingViewModel viewModel)
{
if (!ModelState.IsValid)
{
ViewBag.HowHeardId = new SelectList(db.HowHeard.Where(x => x.Active == true).OrderBy(x => x.SortOrder), "HowHeardId", "HowHeardType", company.HowHeardId);
ViewBag.EventCodeId = new SelectList(db.EventCode.Where(x => x.Active == true).OrderBy(x => x.EventCodeDate), "EventCodeId", "EventCodeName", company.EventCodeId);
return View(viewModel);
}
Company company = await db.Company.FindAsync(viewModel.CompanyId);
if(company == null) {
return HttpNotFound();
}
company.HowHeardId = viewModel.HowHeardId;
company.eNewsletter = viewModel.eNewsletter;
// etc.
// don't need to assign a new value to properties that should be retained
db.Entry(company).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
Update:
You are returning an object of type Company from your GET controller but your view is expecting an EditMarketingViewModel. So do it like this:
// GET: Company/EditMarketing
public async Task<ActionResult> EditMarketing() {
var id = new Guid(User.GetClaimValue("CWD-Company"));
Company company = await db.Company.FindAsync(id);
if(company == null) {
return HttpNotFound();
}
ViewBag.HowHeardId = new SelectList(db.HowHeard.Where(x => x.Active == true).OrderBy(x => x.SortOrder), "HowHeardId", "HowHeardType", company.HowHeardId);
ViewBag.EventCodeId = new SelectList(db.EventCode.Where(x => x.Active == true).OrderBy(x => x.EventCodeDate), "EventCodeId", "EventCodeName", company.EventCodeId);
EditMarketingViewModel viewModel = new EditMarketingViewModel()
{
CompanyId = company.Id,
// Other view model properties go here
}
return View(viewModel);
}
Make sure the object type you are returning from the controller to the view matches.
I am trying to add an image by using MVC 4 ajax form, but it always returns null value. I added my model, controller and view.
My View
#using (Ajax.BeginForm("Create", "Images", new AjaxOptions { HttpMethod = "Post", OnSuccess = "OnSuccess", OnFailure = "OnFailure", UpdateTargetId = "messageDiv" }, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-field">
#Html.LabelFor(model => model.Image.Description, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(model => model.Image.Description)
</div>
#Html.TextBoxFor(model => model.FileImage, new { type = "file" })
#Html.ValidationMessageFor(model => model.FileImage)
</div>...
}
My Model
public class ImageViewModel
{
public IList<Image> Images { get; set; }
public Image Image { get; set; }
public HttpPostedFileBase FileImage { get; set; }
}
My Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ImageViewModel model, HttpPostedFileBase FileImage)
{
if (ModelState.IsValid)
{
var x = FileImage.FileName;
var imageUrl = Path.GetFileName(model.FileImage.FileName);
...
}
}
In this example, I could not get the FileName. it returns always null value. Could you help me to solve this problem. I will be very happy.
I would prefer read the image using the request intead of trying to bind that to a model,
public ActionResult Create(ImageViewModel model)
{
if (Request.Files != null)
{
HttpPostedFileBase file = Request.Files[0]; //assuming that's going to be the first file
if (file.ContentLength > 0)
{
string fileName = Path.GetFileName(file.FileName);
string directory = Server.MapPath("/"); //change ths to your actual upload folder
file.SaveAs(Path.Combine(directory, fileName));
}
}
}