populate view based on multi-level model - asp.net

Suppose, I have models:
public class Person
{
public sting Name {get;set;}
public List<Book> Books {get;set;}
}
public class Book
{
public sting NameBook {get;set;}
}
How represent view for Edit method based on Person model (MVC 3)?

You may try something along the lines of:
#model Person
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
</div>
#Html.EditorFor(x => x.Book)
<button type="submit">Edit</button>
}
and then you will define an editor template for the Book type (~/Views/Shared/EditorTemplates/Book.cshtml) which will be rendered for each element of the Book property collection (which by the way you would have named Books in order to follow standard conventions) on your view model:
#model Book
<div>
#Html.LabelFor(x => x.NameBook)
#Html.EditorFor(x => x.NameBook)
</div>
As far as your controller actions are concerned, it's pretty standard stuff:
public ActionResult Edit(int id)
{
var person = _personRepository.Get(id);
return View(model);
}
[HttpPost]
public ActionResult Edit(Person person)
{
if (!ModelState.IsValid)
{
return View(person);
}
_personRepository.Update(person);
return RedirectToAction("Success");
}

Related

empty return variable in controller - asp.net

Although home.dilstring data is displayed in html, when I check the data with console.write, it shows an empty value. view in html.
Why does it appear empty inside the controller of your class when loading?
Controller:
public class HomeController : BaseController
{
[HttpGet]
public ActionResult Index()
{
Home home = new Home();
using (var db2 = new DBservice...())
{
Console.Write("deneme", home.dilstring);
}
}
}
Model class:
public class Home
{
[Display(Name = "language", ResourceType = typeof(Localization))]
public string dilstring { get; set; }
}
View:
#model ...Models.Home
<p class="vc_subtitle">
#Html.LabelFor(model => model.dilstring)
</p>
home object is newly instantiated object from Home class so "dilstring" property value will be empty if not assigned with default value
LabelFor will just create label element for the specified property and show it's name
so #Html.LabelFor(model => model.dilstring) will generate html below
<label for="dilstring">dilstring</label>
if you change "LabelFor" with "DisplayFor" you will get empty value as controller
to Understand the difference try below
public class HomeController : BaseController
{
[HttpGet]
public ActionResult Index()
{
Home home = new Home();
using (var db2 = new DBservice...())
{
Console.Write("deneme", home.dilstring);
home.dilstring = "Hi";
Console.Write("deneme", home.dilstring);
}
}
return View(home);
}
And for view
#model ...Models.Home
<p class="vc_subtitle">
#Html.LabelFor(model => model.dilstring)
</p>
<p class="vc_subtitle">
#Html.DisplayFor(model => model.dilstring)
</p>

Ambient form data in ASP.NET MVC

I came across a problem with ambient form data, which I've tried to isolate into the following example.
View model:
public class ViewModel
{
public int Value { get; set; }
}
Controller:
public class TestController
{
public ViewResult Test1()
{
return(View(new ViewModel {Value = 1}));
}
[HttpPost]
public ViewResult Test2(ViewModel vm)
{
vm.Value = 2;
return(View(vm));
}
}
Test 1 view:
#model ViewModel
#using(Html.BeginForm("Test2", "Test"))
{
#Html.TextBoxFor(m => m.Value)
<button type="submit">Send</button>
}
Test 2 view:
#model ViewModel
#Model.Value
#Html.TextBoxFor(m => m.Value)
Submitting the form in view 1 gives the, at least to me, surprising result in view 2:
Seems like the posted value gets used in the TextBoxFor().
Anyone knows what's going on here and how to avoid it?

MVC Partial View Validation

I am trying to understand partial views in MVC...
What I am trying to accomplish is to have a master View which renders, say, two partial views.
Each partial view contains a different ViewModel (with DataAnnotations). When I submit the form of one of those partial views, in case there is a server-side validation error, I would like to have the master View show up again with the validation messages on that partial.
Any tips in the right way would be deeply appreciated.
Here you go with the sample solution -
Let create a complex model in following way -
public class Person
{
public Contact contact { get; set; }
public Vehicle vehicle { get; set; }
}
public class Contact
{
[Required]
public string Email { get; set; }
}
public class Vehicle
{
[Required]
public string Name { get; set; }
}
Then lets create a Main controller with an Index action in following way, this action is going to create a simple dummy model and bind it to the Index view -
public class MainController : Controller
{
public ActionResult Index()
{
Person p = new Person();
p.contact = new Contact();
p.vehicle = new Vehicle();
return View(p);
}
}
And Index view is going to be -
#model MVC.Controllers.Person
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Submit","Main",FormMethod.Post))
{
#Html.EditorFor(x => x.contact, "~/Views/Main/EditorTemplates/Contact.cshtml")
#Html.EditorFor(x => x.vehicle, "~/Views/Main/EditorTemplates/Vehicle.cshtml")
<input type="submit" value="click"/>
}
Here in the above view, instead of using Partial Views, I used Editor Views. Reason is that Partial views gives very hard experience in Model binding the Complex models.
So I created EditorTemplated folder in Main View folder and placed following files in there.
Contact.cshtml -
#model MVC.Controllers.Contact
#Html.LabelFor(model => model.Email, new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
Vehicle.cshtml -
#model MVC.Controllers.Vehicle
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
With the above setup, we can go and run the application and following screen should show up -
And this form is going to POSTed to Submit Action of Main controller, so this is going to be my submit action -
public ActionResult Submit(Person p)
{
if (!ModelState.IsValid)
return View("Index", p);
else
{
// do something
return View();
}
}
When we click button without entering any value, then validation will trigger and we should see error messages as below -
And in normal valid situations, you can submit the form and then run your business logic.

Asp.net MVC 3 "parameter conversion from type 'System.String' to type failed" when using SelectList Dropdown box

I'm stuck and after looking this up for hours, I think I need more eyeballs.
The situation is the following:
It's an Asp.Net MVC3 with Entity Framework 4 project. And I have two classes. One ConfigurationFile and another one Action. There is a one-to-many relationship between the two. Here is a simplified view on the code:
public class ConfigurationFile
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "uniqueidentifier")]
[Required]
public Guid ActionId { get; set; }
public virtual Models.Action Action { get; set; }
}
public class Action
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string ActionValue { get; set; }
}
Then I want to create a new ConfigurationFile, and are my two controller methods (and at this point, this is 95% Visual Studio 10 generated code):
// db is my context class.
//
// GET: /Configuration/Create
public ActionResult Create()
{
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue");
return View();
}
//
// POST: /Configuration/Create
[HttpPost]
public ActionResult Create(Models.ConfigurationFile configurationfile)
{
if (ModelState.IsValid)
{
configurationfile.Id = Guid.NewGuid();
db.ConfigurationFiles.Add(configurationfile);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue", configurationfile.ActionId);
return View(configurationfile);
}
And here is a snippet of my Create view:
#model MyProject.Areas.ConfigurationFile.Models.ConfigurationFile
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Configuration File</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ActionId, "Action")
</div>
<div class="editor-field">
#Html.DropDownList("ActionId", String.Empty)
#Html.ValidationMessageFor(model => model.ActionId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I open the Create page, I can clearly see that my dropdown for the Action class is fine (correct value -- the Action.Id -- and text -- Action.ActionValue -- ) but when I submit the form, I have the following error: "The parameter conversion from type 'System.String' to type 'MyProject.Models.Action' failed because no type converter can convert between these types."
Help please !!
Right now MVC has no way of connecting your dropdownlist from your view to the ActionId of your ConfigurationFile object.
I would try replacing this line:
#Html.DropDownList("ActionId", String.Empty)
for this
#Html.DropDownListFor(model => model.ActionId, ViewBag.ActionId)
Other than that, I can't think of what else you might have done wrong.
I hope that helps!
This is how I did to circumvent the problem. I just changed my controller this way:
Models.Action act = db.Actions.Find(configurationfile.ActionId);
ModelState.Clear();
configurationfile.Action = act;
TryValidateModel(configurationfile);
And after that, the validation was Ok. A bit hacky (and another possible hit on the DB), but at least, I can keep going.

ModelBinding asp.net MVC List

I have the following class:
public class Movie
{
string Name get; set;
string Director get; set;
IList<String> Tags get; set;
}
How do I bind the tags properties? to a simple imput text, separated by commas. But only to the controller I'am codding, no for the hole application.
Thanks
You could start with writing a custom model binder:
public class MovieModelBinder : DefaultModelBinder
{
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
{
if (propertyDescriptor.Name == "Tags")
{
var values = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
if (values != null)
{
value = values.AttemptedValue.Split(',');
}
}
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
}
}
and then applying it to a particular controller action which is supposed to receive the input:
public ActionResult Index([ModelBinder(typeof(MovieModelBinder))] Movie movie)
{
// The movie model will be correctly bound here => do some processing
}
Now when you send the following GET request:
/index?tags=tag1,tag2,tag3&name=somename&director=somedirector
Or POST request with an HTML <form>:
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
</div>
<div>
#Html.LabelFor(x => x.Director)
#Html.EditorFor(x => x.Director)
</div>
<div>
#Html.LabelFor(x => x.Tags)
#Html.TextBoxFor(x => x.Tags)
</div>
<input type="submit" value="OK" />
}
The Movie model should be bound correctly in the controller action and only inside this controller action.

Resources