MVC strongly typed view data - asp.net-mvc

I have a strongly typed view that creates the form fields like this:
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })
That's fine, X in the above lambda expression relates to a class I've created with a property of 'Name'.
How do I use this same view but have access to the properties of a different class? For example if you imagine I've another class called - UserDetails, and email address is a property of that. How can I do this:
#Html.TextBoxFor(x => x.Email, new { #class = "form-control" })
within the same strongly typed view?

In asp.net mvc you pass model to view like this:
public ActionResult Base()
{
return View(new DerviedOne());
}
And here is your models definitions :
public class BaseModel
{
public int Id { get; set; }
}
public class DerviedOne : BaseModel
{
public string Email { get; set; }
}
public class DerviedTwo : BaseModel
{
public string Name { get; set; }
}
Then you have to create three views:
Base View:
#using Models
#model Models.BaseModel
#{
ViewBag.Title = "Base";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Base</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BaseModel</h4>
<hr/>
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(x=>x.Id)
<input type="submit" value="Create" class="btn btn-default"/>
</div>
</div>
</div>
if(Model is DerviedOne)
{
Html.RenderPartial("DerviedOneView", Model as DerviedOne);
}
if (Model is DerviedTwo)
{
Html.RenderPartial("DerviedTwoView", Model as DerviedTwo);
}
}
Second view:
#model WebApplication.Models.DerviedOne
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DerviedOne</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Third view:
#model WebApplication.Models.DerviedTwo
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DerviedTwo</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}

Related

ASP.NET MVC - Compound Model

I have a trivial web app with the following model:
public class SillyModel
{
public SillyModel()
{ Id = Guid.NewGuid(); Children = new List<SillyModel>(); }
[Key]
public virtual Guid Id { get; set; }
public virtual string Value { get; set; }
public virtual List<SillyModel> Children { get; set; }
}
}
I have an Edit View of:
#model WebApplication1.Models.SillyModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.Partial("EditPartial", Model)
<div class="form-horizontal">
<h4>SillyModel</h4>
<hr />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
With the Partial:
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#foreach (var item in Model.Children)
{
#Html.Partial("EditPartial", item)
}
</div>
The rendering is Fine! But for the life of me (well at least 3 days of struggle) I can not get it so that the returned model is properly bound! [No children are returned]
I am at my wits end.
You have to re-structure the way you are coding a little
In the Views/YourController, add a folder called EditorTemplates if not yet exists and add a view named SillyModel and copy the code from EditPartial to this new view
You change the foreach to for loop to decorate the controls with index
The code
~/Views/YourController/EditorTemplates/SillyModel.cshtml
#model WebApplication1.Models.SillyModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
#for (var index=0; index<Model.Children.Count;index++)
{
#Html.EditorFor(model=>Model.Children[index])
}
</div>
~/Views/YourController/Edit
instead of #Html.Partial("EditPartial", Model), use #Html.EditorFor(m=>m)
Explanation
By adding the EditorTemplates/SillyModel , now you can call #Html.EditorFor(model=>Model.Children[index]) and your custom editor will be rendered
You need to use indexed controls in order Binding to the Model succeeded
Hope this will help you

Submitting image path to the database

Im looking enter in some data and then save it into a DB table,The other fields work just fine when submitted to the database. but when it comes to saving the image path it gives null. Ive searched online and everything i find is out of date and doesn't work for the most part.
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert([Bind(Include ="id, ImageName, ImageSize")] ImageInfo info, HttpPostedFileBase ImagePath)
{
if(ModelState.IsValid)
{
if (ImagePath != null)
{
var filename = Path.GetFileName(ImagePath.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads"), filename);
ImagePath.SaveAs(path);
ImagePath.SaveAs(HttpContext.Server.MapPath("~/Uploads") + ImagePath.FileName);
info.ImagePath = ImagePath.FileName;
}
db.ImageInfoes.Add(info);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(info);
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageSize, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageSize, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageSize, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#*#Html.TextBoxFor(model => model.ImagePath, new { type = "file" })*#
<input type="file" name="ImagePath" id="ImagePath" />
#Html.ValidationMessageFor(model => model.ImagePath, "", new { #class = "text-danger" })
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</div>
</fieldset>
DB
Any help would be great!.
You want to add enctype="multipart/form-data" to form tag.
I personally like to rename HttpPostedFileBase parameter name to file not to confuse with ImageInfo.ImagePath.
View
#using (Html.BeginForm("Index","Start",FormMethod.Post,new {enctype="multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr/>
...
<div class="form-group">
#* Notice name and id are named as file *#
<input type="file" name="file" id="file"/>
#Html.ValidationMessageFor(model => model.ImagePath, "", new {#class = "text-danger"})
</div>
...
</div>
</div>
</fieldset>
}
Model
public class ImageInfo
{
public string Id { get; set; }
public string ImageName { get; set; }
public string ImageSize { get; set; }
public string ImagePath { get; set; }
}
Action Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "id, ImageName, ImageSize")] ImageInfo info,
HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(Server.MapPath("~/Uploads") + file.FileName);
info.ImagePath = file.FileName;
}
/*db.ImageInfoes.Add(info);
db.SaveChanges();*/
return RedirectToAction("Index");
}
return View(info);
}

Upload a HttpPostedFileBase for image but always null

I am new to MVC and web programming.
My uploading image is working fine, but when I use almost the exact same code to allow user to modify its upload, it HttpPostedFileBase is always null. Driving me crazy...
here is the model
public class ModifyGenreViewModel
{
public int Id { get; set; }
[Display(Name = "Nom du style")]
public string Name { get; set; }
[Display(Name = "Image")]
public HttpPostedFileBase Image { get; set; }
}
and the view
#using (Html.BeginForm("ModifyGenre", "Upload", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#Resource.StyleCreationHeader</h4>
<hr />
#Html.ValidationMessageFor(model=>Model)
<div class="form-group" style="display:none">
#Html.LabelFor(model => model.Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Id, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="ModifyGenreViewModel.Image" />
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resource.Modify" class="btn btn-primary" />
</div>
</div>
</div>
}
When I set the breakpoint in my controller, I see the Id and the name, but Image is always null.
Thank's for your help!
The name property value of your file input should match with the view model property name for model binding to work properly.
Change the input field name to "Image"
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="Image" />
Assuming your HttpPost action method accepts the ModifyGenreViewModel object as parameter.
[HttpPost]
public ActionResult ModifyGenre(ModifyGenreViewModel model)
{
// to do : return something
}
In model class change it to public String Image { get; set; }
In .cshtml page change it to #Html.TextBoxFor(m => m.Image, new {
type = "file", name="Image" })
In Controller you should use the name what you have decleared in html helper HttpPostedFileBase Image

ViewBag does not work

I'm trying to create an MVC site and I have some trubble with the ViewBag.
It's like it can't contain elements.
I'm using MVC 4.
This is my function in the controller:
public ActionResult Create()
{
ViewBag.DiscID = new SelectList(entities.Disc, "ID", "ID");
ViewBag.StarID = new SelectList(entities.Star, "ID", "Name");
ViewBag.Num = 7;
return View();
}
This is my Create view:
#model Movies.Models.StarAndRole
#{
ViewBag.Title = "Create";
int num;
int.TryParse(ViewBag.Num, out num);
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>StarAndRole</h4>
<h3>num is = #num</h3>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.StarID, "Star", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("StarID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StarID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DiscID, "Disc Num", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("DiscID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DiscID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
It appears to me that everything I put in the ViewBag is NULL.
What am i doing wrong?
Thank you in advance!
Int.TryParse method accepts a string (representation of the int value). But you are passing a dynamic type to the method. It should give you an error about that.
If you know you are setting an Int value from your action method, in your view, you can simply read it like
int num = ViewBag.Num;
A better solution is to add the data you want to pass to the view to your view model.
public class YourCreateVm
{
public string Description { set;get;}
public int Num { set;get;}
public List<SelectListItem> Disks { set;get;}
public int SelectedDiskId { set;get;}
}
and in your GET view
public ActionResult Create()
{
var vm = new YourCreateVm { Num = 7 };
vm.Disks = entities.Disc.Select(s=> new SelectListItem
{ Value=s.ID.ToString(), Text =s.Name}).ToList();
return View(vm);
}
and in your view
#model YourCreateVm
#using(Html.BeginForm())
{
<p>My num is : #Model.Num <p>
#Html.TextBoxFor(s=>s.Description)
#Html.DropDownListFor(s=>s.SelectedDiskId, Model.Disks)
<input type="submit" />
}

Don't fire a required validator for

I have the following model:
public class Model1
{
[Required(ErrorMessage="E1")]
public string Name { get; set; }
[Required(ErrorMessage="E2")]
[RegularExpression(".+\\#.+\\..+")]
public string Email { get; set; }
[Required(ErrorMessage="E3")]
public bool WillAttend { get; set; }
}
controller action:
public ActionResult Model1()
{
Model1 m = new Model1();
return View(m);
}
and view:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Model1</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WillAttend, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.WillAttend)
#Html.ValidationMessageFor(model => model.WillAttend, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
problem is required validator for WillAttend property does not work. Even in action method, ModelState.IsValid is true. Why and how to do WillAttend is required ?
At the moment, your required-attribute just checks whether a value was specified. Since a boolean is either true or false, the validation will never fail.
You can, mark the boolean as nullable:
public bool? WillAttend { get; set; }
or you can try to make a custom validator if you are trying to force them to check the box, such as in this link.

Resources