Loading a page with values from multiple tables using a ViewModel - c#

What I want to do is to have a number of labels populate with information from 2 different tables. When the submit button is pressed, it will then hit my Post method and save the info the user typed in and the info automatically populated to the db, but I'm having issues with my Get method. My Get method in my controller looks like this:
[HttpGet]
public ActionResult AddOrganization(int peopleID = 1)
{
var peopleModel = db.People.Include("EmployeeContacts").Single(g => g.PeopleID == peopleID);
return View("../Setup/AddOrganization", peopleModel);
}
However, I've got a viewModel that looks like this and includes all the tables that will be needed for the Get and Post methods for this page:
public class AddOrganizationViewModel
{
public MusicStore.Models.Organizations Organizations { get; set; }
public MusicStore.Models.People People { get; set; }
public MusicStore.Models.OrganizationOptions OrganizationsOptions { get; set; }
public MusicStore.Models.EmployeeContacts EmployeeContacts { get; set; }
}
So when the view is first loaded, and the Get method above is called, I get an error saying
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.People_8080C33752A42A0C994331F5015BCCFCEB99B3ECD7AB8CA2BB11ABE67851B81B', but this dictionary requires a model item of type 'MusicStore.ViewModels.AddOrganizationViewModel'.
Here is my view also:
<h2>AddOrganization</h2>
#model MusicStore.ViewModels.AddOrganizationViewModel
#using (Html.BeginForm("Add", "AddOrganization"))
{
<fieldset>
<legend>CONTACT INFORMATION</legend>
<div class ="editor-label">
#Html.Label("Organization Name")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.Organizations.OrgName)
</div>
<div class ="editor-label">
#Html.Label("Phone Number")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.Organizations.OrgPhone)
</div>
<div class ="editor-label">
#Html.Label("Point of Contact")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.Organizations.OrgPointOfContact)
</div>
<div class ="editor-label">
#Html.Label("Office Location")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.Organizations.OrgOfficeLocation)
</div>
</fieldset>
<fieldset>
<legend>FIRST EMPLOYEE OF ORGANIZATION</legend>
<div class ="editor-label">
#Html.Label("Admin First Name")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.People.FirstName)
</div>
<div class ="editor-label">
#Html.Label("Admin Last Name")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.People.LastName)
</div>
<div class ="editor-label">
#Html.Label("Admin NID")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.People.NID)
</div>
<div class ="editor-label">
#Html.Label("Admin SID")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.People.SID)
</div>
<div class ="editor-label">
#Html.Label("Admin Email")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.EmployeeContacts.Email)
</div>
<div class ="editor-label">
#Html.Label("Admin Phone Number")
</div>
<div class ="editor-field">
#Html.TextBoxFor(Model => Model.EmployeeContacts.PrimaryPhone)
</div>
How do I send the needed information to the view via the Get method when it can only take a ViewModel type? Is there a way I can add this information to my ViewModel? Any help or shove in the right direction would great.

var peopleModel = db.People.Include("EmployeeContacts").Single(g => g.PeopleID == peopleID);
what is the type of peopleModel in this case?
Seeing as you are returning it directly from a database, it looks like it has not yet been 'converted' or 'projected' into the actual viewmodel that the view expects.
you will probably need to do something like this:
AddOrganizationViewModel vm = new AddOrganizationViewModel();
vm.Organizations = new Organizations()
{
someOrganizationProperty = peopleModel.SomeProperty,
};
vm.People = new People()
{
somePeopleProperty = peopleModel.SomeOtherProperty,
};
//etc for the other properties and types in your viewmodel
when all that data has been set, you can return the AddOrganizationViewModel.

var peopleModel = db.People.Include("EmployeeContacts").Single(g => g.PeopleID == peopleID);
This doesn't create an AddOrganizationViewModel object. Either add a Select clause at the end or use the data in that object to populate a new viewmodel.

Related

Walk around for assigning attributes dynamically to ViewModel in MVC

I have ViewModel that is closely related to BLL model. So now I have:
public class CarViewModel
{
[Required]
public string Colour { get; set; }
public int MaxSpeed { get; set; }
}
It is displayed on page as simple panel:
<div class="card">
<div class="card-content">
<form method="post" class="col s12" id="sim_house_seeker">
<div class="row">
<div class="input-field col s6">
<i class="material-icons prefix">phone</i>
#Html.TextBoxFor(m => m.Colour)
#Html.LabelFor(m => m.Colour )
</div>
<div class="input-field col s6">
<i class="material-icons prefix">credit_card</i>
#Html.TextBoxFor(m => m.MaximalPrice)
#Html.LabelFor(m => m.MaximalPrice)
</div>
</div>
<button type="submit" class="waves-effect waves-light btn-large">Save</button>
</form>
<div class="clearBoth"></div>
</div>
Now I would like to be able to use that ViewModel do display another data, so I would like to do this:
public class PanelViewModel
{
[Required]
public string LeftSideOfPanel { get; set; }
public int RightSideOfPanel { get; set; }
}
And ViewModel:
<div class="card">
<div class="card-content">
<form method="post" class="col s12" id="sim_house_seeker">
<div class="row">
<div class="input-field col s6">
<i class="material-icons prefix">phone</i>
#Html.TextBoxFor(m => m.LeftSideOfPanel )
#Html.LabelFor(m => m.LeftSideOfPanel )
</div>
<div class="input-field col s6">
<i class="material-icons prefix">credit_card</i>
#Html.TextBoxFor(m => m.RightSideOfPanel )
#Html.LabelFor(m => m.RightSideOfPanel )
</div>
</div>
<button type="submit" class="waves-effect waves-light btn-large">Save</button>
</form>
<div class="clearBoth"></div>
</div>
This gives my ability to use that panel in any model that I want to and it separates ViewModel from BLL entity. But my problem is that I add validation attributes to my ViewModel that can be different in models. I know that it is not possible to add attributes dynamically so what is the best walkaround for this problem?
View Models are more closely tied to views as compared to business layer, you can create panelviewmodel interface and then viewmodels with validations accordingly
Validation attributes are nice in a sense that the model state is updated automatically upon binding, note however that they are completely optional. Instead of relying on auto validation, you can perform validation on your own, each time based on different rules.
Down the pipeline this is completely equivalent - your views will show model errors regardless of how errors are added (by auto validation based on attributes or based on manual validatiin based on whatever you want).
What I suggest then is that instead of trying to assign validation attributes dynamically, just skip this and have your own validation code that add errors just like the attribute-based engine would add them automatically:
public ActionResult Foo( YourModel model )
{
// manual validation
if ( ... )
ModelState.AddModelError( key, error );
if ( ModelState.IsValid ) // this works too
...
where keys will refer to your model properties.

Issue when creating a reference of a Model without using Html.EditorFor() asp.net mvc

I am facing an issue when trying to create a reference of my model Restriction:
public int RestrictionID
public string portefeuille
public int AssetID
public int SegmentID
public int SubAssetID
public int Min
public int Max
public string logic_op
public virtual Asset Asset
public virtual Segment Segment
public virtual SubAsset SubAsset
That is instead of using the normal template in create view with #Html.EditorFor I am using a dropdown list with a script behind to fill the dropdown depending on the previous selected item that's work well, but when submitting nothing happen no error no redirecting, and of course the reference is not added to the database.
Here is my Create view:
<div class="editor-label">
#Html.LabelFor(model => model.portefeuille)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.portefeuille)
#Html.ValidationMessageFor(model => model.portefeuille)
</div>
#Html.DropDownList("Asset", ViewBag.AssetID as SelectList, "Select a Asset Class", new { id="Asset" })<br />
<select id="Segment" name="segment"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAsset"></select><br />
<div class="editor-label">
#Html.LabelFor(model => model.Min)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Min)
#Html.ValidationMessageFor(model => model.Min)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Max)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Max)
#Html.ValidationMessageFor(model => model.Max)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.logic_op)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.logic_op)
#Html.ValidationMessageFor(model => model.logic_op)
</div>
and my RestrictionController: ( I omitted useless parts)
public ActionResult Create()
{
ViewBag.AssetID = new SelectList(db.Assets, "AssetID", "Asset_Name");
return View();
}
//
// POST: /Restriction/Create
[HttpPost]
public ActionResult Create(Restriction restriction)
{
if (ModelState.IsValid)
{
db.Restrictions.Add(restriction);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.AssetID = new SelectList(db.Assets, "AssetID", "Asset_Name", restriction.AssetID);
return View(restriction);
}
Can somebody help to find where is the problem ?
Thank you for your help!
Name of the property in the model a should be same as name of the element in the html in order to bind values after the post back.That is why the code below
<select id="Segment" name="segment"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAsset"></select><br />
should be replaced by.
<select id="Segment" name="SegmentID"></select><br /> //Here I am not using Html.EditorFor as EF does
<select id="subAsset" name="SubAssetID"></select><br />
Always look at the HTML generated by any technology. This will help you to understand how the things are working.

Why is my model not passed as parameter with form post

I'm having trouble understanding why my model is not passed along with its values to my controller when posting a form.
I have a view with a strongly typed model (UnitContract) that is being fetched from a webservice, that holds a set of values. In my action I'm trying to fetch int ID and bool Disabled fields that exists in my model. When debugging, I see that my model being passed from the form doesn't contain any values at all. What am I missing?
My view (UnitContract as strongly typed model):
...
<form class="pull-right" action="~/UnitDetails/EnableDisableUnit" method="POST">
<input type="submit" class="k-button" value="Enable Unit"/>
</form>
My controller action:
[HttpPost]
public ActionResult EnableDisableUnit(UnitContract model)
{
var client = new UnitServiceClient();
if (model.Disabled)
{
client.EnableUnit(model.Id);
}
else
{
client.DisableUnit(model.Id);
}
return RedirectToAction("Index", model.Id);
}
Sounds like you need to add the fields from your model to your form. Assuming your view accepts a UnitContract model, then something like this should work:
<form class="pull-right" action="~/UnitDetails/EnableDisableUnit" method="POST">
#Html.HiddenFor(x => x.Id)
#Html.HiddenFor(x => x.Disabled)
<input type="submit" class="k-button" value="Enable Unit"/>
</form>
Now when you submit the form, it should submit the fields to your model.
The MVC framework will use the data from the form to create the model. As your form is essentially empty, there is no data to create the model from, so you get an object without any data populated.
The only data that is sent from the browser in the request when you post the form, is the data that is inside the form. You have to put the data for the properties in the model as fields in the form, so that there is something to populate the model with.
Look into using #Html.HiddenFor(). Put these in your form, and the data you want to see posted back to your controller should be there. For example, your form would look something like...
<form class="pull-right" action="~/UnitDetails/EnableDisableUnit" method="POST">
#Html.HiddenFor(x => x.Id)
#Html.HiddenFor(x => x.IsDisabled)
<input type="submit" class="k-button" value="Enable Unit"/>
</form>
Let's say you have a model like this:
public class UnitContract
{
public int Id { get; set; }
public DateTime SignedOn { get; set; }
public string UnitName { get; set; }
}
Your view would look something like this:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>UnitContract</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.SignedOn)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SignedOn)
#Html.ValidationMessageFor(model => model.SignedOn)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UnitName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UnitName)
#Html.ValidationMessageFor(model => model.UnitName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
In your controller:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Edit(UnitContract unitContract)
{
// do your business here .... unitContract.Id has a value at this point
return View();
}
Hope this is helpful.

#Html.ValidationMessageFor crashes when I try to edit an item

Why is my #Html.ValidationMessageFo not working? When I run the application, nothing happens and it allows everything to be entered. And it also crashes when I try to edit an item in my edit view, which is below. I have the following:
<div class="editor-label">
#* #Html.LabelFor(model => model.Posted)*#
</div>
<div class="editor-field">
#Html.HiddenFor(model => model.Posted, Model.Posted = DateTime.Now)
#Html.ValidationMessageFor(model => model.sendinghome)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Cartypes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Cartypes)
#Html.ValidationMessageFor(model => model.Cartypes)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RegNum)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RegNum)
#Html.ValidationMessageFor(model => model.RegNum)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Regprice)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Image)
#Html.ValidationMessageFor(model => model.Regprice)
</div>
Here is how validation works.
Let's say you have the following model:
public class MyModel {
[Required]
public string MyProperty { get; set; }
}
Note the Required attribute, it is a data annotation attribute that specifies that MyProperty is a required field.
MyModel is used by the following view (MyView.cshtml):
#model MyNamespace.MyModel
#using (Html.BeginForm("MyAction", "MyController")) {
#Html.LabelFor(m => m.MyProperty)
#Html.TextBoxFor(m => m.MyProperty)
#Html.ValidationMessageFor(m => m.MyProperty)
<input type="submit" value="Click me">
}
Then, when this form gets posted to the MyAction action of MyController, the validation of your model will be performed. What you have to do is check whether your model is valid or not.
It can be done using the ModelState.IsValid property.
[HttpPost]
public ActionResult MyAction(MyModel model) {
if (ModelState.IsValid) {
// save to db, for instance
return RedirectToAction("AnotherAction");
}
// model is not valid
return View("MyView", model);
}
If the validation failed, the view will be rendered again using the different errors that are present in the ModelState object. Those errors will be used and displayed by the ValidationMessageFor helper.
Exactly, Bertrand explains it right, you could also use jquery validation too and eliminate the calls to the server validating on the browser. (asp.net mvc takes care of validating the rules on your model automatically)

AllowHtml attribute not working

I have a model with this property:
[AllowHtml]
[DisplayName("Widget for Table")]
[StringLength(1000, ErrorMessage = "Maximum chars 1000")]
[DataType(DataType.Html)]
public object TableWidget { get; set; }
And here is the create methods in controller:
//
// GET: /Admin/Table/Create
public ActionResult Create(int id)
{
Season season = _seasonRepository.GetSeason(id);
var table = new Table
{
SeasonId = season.SeasonId
};
return View(table);
}
//
// POST: /Admin/Table/Create
[HttpPost]
public ActionResult Create(Table a)
{
if (ModelState.IsValid)
{
_tableRepository.Add(a);
_tableRepository.Save();
return RedirectToAction("Details", "Season", new { id = a.SeasonId });
}
return View();
}
And last here is my view:
#model Stridh.Data.Models.Table
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Fields</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.TableURL)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TableURL) #Html.ValidationMessageFor(model => model.TableURL)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SortOrder)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SortOrder) #Html.ValidationMessageFor(model => model.SortOrder)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TableWidget)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TableWidget) #Html.ValidationMessageFor(model => model.TableWidget)
</div>
<div class="editor-label invisible">
#Html.LabelFor(model => model.SeasonId)
</div>
<div class="editor-field invisible">
#Html.EditorFor(model => model.SeasonId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I add a "normal" message without html everything is saved OK, but when saving it says A potentially dangerous Request.Form...
Another strange thing is that I got this [AllowHtml] to work in another model class. I cant find why this is causing me troubble. Need your help. :-)
The way you are using AllowHtml should work. Make sure that you are not accessing the HttpRequest.Form collection anywhere else in your code (controller, filter, etc) as this will trigger ASP.NET Request Validation and the error you are seeing. If you do want access to that variable then you should access it via the following code.
using System.Web.Helpers;
HttpRequestBase request = .. // the request object
request.Unvalidated().Form;
I get the same problem and i solve it with the help of this post.
If you are on .net 4.0 make sure you add this in your web.config
<httpRuntime requestValidationMode="2.0" />
Inside the <system.web> tags
I had the same problem. My model class is named "GeneralContent" and has the property "Content". In my action method i used attribute like this:
public ActionResult Update(GeneralContent content)
when i renamed content argument to cnt, everything works well. I think MVC is confused when some attribude of model class has the same name as the argument in action method.
I also had this issue. I could not get a model property marked with [AllowHtml] to actually allow HTML, and instead encountered the same error you describe. My solution ended up being to mark the Controller action that accepts the posted model with the [ValidateInput(false)] attribute.
The answer that #marcind put me on the right track but my issue was that I was passing the FormCollection into the Controller method, so changing this...
public ActionResult Edit(MyClass myClass, FormCollection collection)
To this...
public ActionResult Edit(MyClass myClass)
Solved the problem.
Subsequently, I was able to access the heck out of the form collection with code like this without issue.
foreach (var key in Request.Form.AllKeys)
{
...
}
So, it was the passing the form collection parameter that caused the problem, not merely accessing the form collection.

Categories

Resources