MVC3 partial view with ViewModel property as model - asp.net-mvc-3

I'm trying to pass a ViewModel property to a partial view, but getting the following error:
"The model item passed into the dictionary is of type '<>f__AnonymousType2`1[DomaniOnline.Models.DomaniData.TempRates]', but this dictionary requires a model item of type 'DomaniOnline.Models.DomaniData.TempRates'."
How do I pass the VM property so that it is not an anonymous type?
The View:
#model DomaniOnline.Models.ViewModels.CompareRatesViewModel
#{
ViewBag.Title = "Rate Comparison";
}
<h2>Compare Rates</h2>
<table>
<tr>
<td>#Html.DisplayTextFor(m=>m.TempRate1.CarrierName)</td>
<td>#Html.DisplayTextFor(m=>m.TempRate2.CarrierName)</td>
<td>#Html.DisplayTextFor(m=>m.TempRate3.CarrierName)</td>
<td>#Html.DisplayTextFor(m=>m.TempRate4.CarrierName)</td>
</tr>
<tr>
<td>#Html.Partial("_TempRatesPartial", new { tempRate = Model.TempRate1 })</td>
<td>#Html.Partial("_TempRatesPartial", new { tempRate = Model.TempRate2 })</td>
<td>#Html.Partial("_TempRatesPartial", new { tempRate = Model.TempRate3 })</td>
<td>#Html.Partial("_TempRatesPartial", new { tempRate = Model.TempRate4 })</td>
</tr>
</table>
The Partial View:
#model DomaniOnline.Models.DomaniData.TempRates
<fieldset>
<legend>TempRates</legend>
<div class="display-label">Carrier Name</div>
<div class="display-field">
#Html.DisplayFor(model => model.CarrierName)
</div>
....
</fieldset>
And the ViewModel:
public class CompareRatesViewModel
{
public TempRates TempRate1 { get; set; }
public TempRates TempRate2 { get; set; }
public TempRates TempRate3 { get; set; }
public TempRates TempRate4 { get; set; }
public TempRates TempRate5 { get; set; }
public CompareRatesViewModel(IEnumerable<TempRates> TempRateList)
{
this.TempRate1 = TempRateList[0];
this.TempRate2 = TempRateList[1];
this.TempRate3 = TempRateList[2];
this.TempRate4 = TempRateList[3];
this.TempRate5 = TempRateList[4];
}
}

Why not just pass in the object directly rather than using an anonymous type?
Your partial takes TempRates and your TempRate1 is of type TempRate so you should be able to do this without casting.
<td>#Html.Partial("_TempRatesPartial", Model.TempRate1)</td>

You need to cast your anonymous type as the type that is the model of your partial view:
#Html.Partial("_TempRatesPartial", (DomaniOnline.Models.DomaniData.TempRates)Model.TempRate1)

Related

ASP.NET Core 6: #Html.DropDownList analogue for one item for binding multiple models in one view

ASP.NET Core 6 MVC / EF model-first
Is there simple analogue of #Html.DropDownList and ViewBag to replace IDs from one model to values from another model?
I have got two models:
Model1
public int ID { get; set; } // PK
public int PersonID { get; set; } // FK
ID
PersonID
1
3
2
4
Model2
public int PersonID { get; set; } // PK
public int Name { get; set; }
PersonID
Name
3
Nick
4
James
Controller:
public async Task<IActionResult> Index()
{
return View(await _context.Model1.ToListAsync());
}
View:
#model IEnumerable<wpg.monster.Models.Model1>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.PersonID)
</td>
</tr>
}
So I'll get
TRHead
TRHead
1
3
2
4
Is there any trick to replace 3 and 4 with Names from Model2, like ViewBag with #Html.DropDownList to see something like this in View? Or there is only one way to create ViewModel?
TRHead
TRHead
1
Nick
2
James
Here a possible method for you:
First create a model3 to collect the information from model1 and model2:
public class Model1
{
public int ID { get; set; }
public int PersonId { get; set; }
}
public class Model2
{
public int PersonId { get; set; }
public string Name { get; set; }
}
public class Model3
{
public int ID { get; set; }
public string Name { get; set; }
}
Then in your controller, you can select the data you want and insert them to the new model which will be displayed on the viewer, here you can combine two elements whose PersonId are the same to the new model3:
public List<Model3> GetModel3()
{
List<Model1> model1 = new();
List<Model2> model2 = new();
List<Model3> model3 = new();
model1.Add(new Model1 { ID = 1, PersonId = 3 });
model1.Add(new Model1 { ID = 2, PersonId = 4 });
model2.Add(new Model2 { PersonId = 3, Name = "Nick" });
model2.Add(new Model2 { PersonId = 4, Name = "James" });
foreach (Model1 m1 in model1)
{
foreach (Model2 m2 in model2)
{
if(m1.PersonId == m2.PersonId)
{ model3.Add(new Model3 { ID = m1.ID, Name = m2.Name }); }
}
}
return model3;
}
Then you should use the following method in controller to pass the new model to the viewer:
public IActionResult Test()
{
ViewBag.Message = "This is test demo";
dynamic model = new ExpandoObject();
model.Model3 = GetModel3();
return View(model);
}
Last is the viewer page:
#using Test;
#using Test.Models;
#model dynamic
#{
ViewBag.Title = "Test";
ViewData["Title"] = "Test";
}
<h2>#ViewBag.Message</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
#foreach (Model3 model3 in Model.Model3)
{
<tr>
<td>#model3.ID</td>
<td>#model3.Name</td>
</tr>
}
</table>
Now you may solve your issue, here is my test to it:

Set enum values as selected in Razor dropdown

I am using a razor view for showing the scored of an examination and it contains an enum value that holds 3 values "pass","Fail","absent" and i want to choose it accordingly. The model i used is
model
public class VerifyResultModel
{
[Display(Name = "StudnetId")]
public int StudentId { get; set; }
[Display(Name = "Student")]
[Required]
public string Student { get; set; }
[Display(Name = "Mark")]
[Required]
public int Mark { get; set; }
[Display(Name = "Score")]
[Required]
public string Score { get; set; }
[Display(Name = "Result")]
public App.EnumValues.ExamResultStatus Result { get; set; }
}
Controller
[HttpGet]
public ActionResult Verify(int Id)
{
List<VerifyResultModel> model_result = new List<VerifyResultModel>();
VerifyResultModel _resultItem;
foreach (exammark item in marks)
{
SchoolApp.EnumValues.ExamResultStatus result = SchoolApp.EnumValues.ExamResultStatus.Absent;
if(item.Mark >= MinMark)
{
result= SchoolApp.EnumValues.ExamResultStatus.Pass;
}
else
{
result = App.EnumValues.ExamResultStatus.Fail;
}
_resultItem = new VerifyResultModel { StudentId = (int)item.StudentId, Student = item.studentmaster.StudentName, Mark = (int)item.Mark, Score = item.Mark.ToString(), Result = result };
model_result.Add(_resultItem);
}
LoadResultsDropdown();
return View(model_result);
}
private void LoadResultsDropdown()
{
var types = (from App.EnumValues.ExamResultStatus type in Enum.GetValues(typeof(SchoolApp.EnumValues.ExamResultStatus))
select new { Id = type.ToString(), Name = type.ToString() }).ToList();
ViewBag.ResultList = new SelectList(types, "Id", "Name");
}
View
#model IList<SchoolApp.ViewModels.VerifyResultModel>
<tbody>
#for (int item = 0; item < Model.Count(); item++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[item].Student)
</td>
<td>
#Html.DisplayFor(modelItem => Model[item].Mark)
</td>*#
<td>
#Html.DisplayFor(modelItem => Model[item].Score)
</td>
<td>
#Html.DropDownListFor(model => model[item].Result, (SelectList)ViewBag.ResultList) //Here All values are showing as Pass ( first item in dropdown)
</td>
</tr>
}
</tbody>
The problem is even if i pass values Fail / Absent to enum it shows as Pass in combobox . How can i show the correct value ?
Can you please try this:
private void LoadResultsDropdown()
{
var types = (from App.EnumValues.ExamResultStatus type in Enum.GetValues(typeof(SchoolApp.EnumValues.ExamResultStatus))
select new { Id = type.ToString(), Name = type.ToString() }).ToList();
var types=Enum.GetValues(typeof(ExamResultStatus)).Cast<ExamResultStatus>();
var EnumData= types.Select(c => new { c.Key, c.Value });
ViewBag.ResultList = new SelectList(types.AsEnumerable(), "key", "value");
}
Hope this helps..

How to access IQueryable in Razor?

I’m building MVC3 “code first” application and to display data on my page I’m using the following:
Controller Code:
public ViewResult Index()
{
dynamic assignedT = from t in db.AssignedTasks
join a in db.Approvers on t.ApproverID equals a.ID
join r in db.Requestors on t.RequestorID equals r.ID
select new {Approver =a.FirstName + " " +a.LastName,
Requestor=r.FirstName + " " + r.LastName,
Title = t.Title,
t.RequestedDate,
t.CompletedDate, t.Description,
Status =(int)t.InternalStatut
};
return View(assignedT);
}
and on the page:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
#foreach (var item in Model) {
<tr>
<td>#item.GetType().GetProperty("Title").GetValue(item, null)
</td>
</tr>
}
</table>
I know that using dynamic is not the best way.
How to achieve same functionality using strongly-typed view?
Thank you
You can define the following class:
public class MyViewModel
{
string Approver { get; set; }
string Requestor { get; set; }
string Title { get; set; }
DateTime RequestedDate { get; set; }
DateTime CompletedDate { get; set; }
string Description { get; set; }
int Status { get; set; }
}
If you then change select new in your Linq query to select new MyViewModel then you have a strongly typed viewmodel (and dynamic to var).

CheckBoxList does not update the model

I defined a Person entity:
public partial class Person
{
public string persID { get; set; }
public string last_name { get; set; }
public string driving_licence { get; set; }
}
where the driving licence is as follows:
public class DrivingLicence
{
public string drivingLicenceValue { get; set; }
public string drivingLicenceText { get; set; }
public DrivingLicence(string paValue, string paText)
{
drivingLicenceValue = paValue;
drivingLicenceText = paText;
}
}
having a repository where is defined this function:
public List<DrivingLicence> GetAll()
{
try
{
var drivingLicenceList = new List<DrivingLicence>();
DrivingLicence oneDrivingLicence = new DrivingLicence("A", "A");
drivingLicenceList.Add(oneDrivingLicence );
oneDrivingLicence = new DrivingLicence("B", "B");
drivingLicenceList.Add(oneDrivingLicence );
oneDrivingLicence = new DrivingLicence("C", "C");
drivingLicenceList.Add(oneDrivingLicence );
oneDrivingLicence = new DrivingLicence("D", "D");
drivingLicenceList.Add(oneDrivingLicence );
return drivingLicenceList;
}
catch (Exception)
{
throw new Exception("An error occured. Failed to Get the list.");
}
}
Now: I want the driving licences displayed as a CheckBoxList and on submit I want the person to get assigned the checked driving licence categories, e.g.: the "A" and "C" categories are selected, the resulting person.driving_licence must be "AC".
The problem is that this does not happen, the person is created but the driving_licence property is empty. I payed attention that the check boxes name be identical to that of the corresponding property (Person.driving_licence).
Is that an error in the present code? Or should I modify the Person entity?
Thank you for your advice.
Here is the view model:
public class PersonFormViewModel
{
// Properties
public Person person { get; set; }
public SelectList DrivingLicenceList { get; set; }
public string ActionToPerform { get; set; }
public PersonFormViewModel() { }
// Constructor
public PersonFormViewModel(Person pPerson, SelectList pDrivingLicenceList)
{
person= pPerson;
DrivingLicenceList = pDrivingLicenceList;
if (String.IsNullOrEmpty(person.persID))
{
ActionToPerform = "Create";
}
else
{
ActionToPerform = "Edit";
}
}
}
The controller:
//
// GET: /Person/Create
[Authorize]
public ActionResult Create()
{
Person person = new Person();
SelectList drvLicenceList = new SelectList(drvLicenceRepository.GetAll(), "drivingLicenceValue", "drivingLicenceText");
return View("Create", new PersonFormViewModel(person, drvLicenceList));
}
//
// POST: /Person/Create
[HttpPost, Authorize]
public ActionResult Create(PersonFormViewModel model)
{
Person person = model.person;
SelectList drvLicenceList = new SelectList(drvLicenceRepository.GetAll(), "drivingLicenceValue", "drivingLicenceText");
if (ModelState.IsValid)
{
try
{
db.Entry(person).State = EntityState.Added;
db.SaveChanges();
return RedirectToAction("Details");
}
catch (...)
{
...
}
}
return View("Create", new PersonFormViewModel(person, drvLicenceList));
}
And the view:
#model MyApp.ViewModels.PersonFormViewModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(false, "Errors occured.")
<fieldset>
<legend>Fill in your details</legend>
#Html.LabelFor(model => model.person.last_name)
#Html.TextBoxFor(model => model.person.last_name)
#Html.ValidationMessageFor(model => model.person.last_name, "*")
#Html.HiddenFor(model => model.person.persID)
#foreach (var ctg in (Model.DrivingLicenceList))
{
<input type="checkbox" name="driving_licence" value=ctg.value />#ctg.Text
}
<input type="submit" value="Sauvegarder" class="submit" />
</fieldset>
}
I would use a collection property in order to store the selected driving licence categories (multiple checkboxes can be selected => collection):
public partial class Person
{
public string persID { get; set; }
public string last_name { get; set; }
public string[] driving_licence { get; set; }
}
and then you will need to fix the name of the checkbox in order for it to bind correctly:
#foreach (var ctg in Model.DrivingLicenceList)
{
<input type="checkbox" name="person.driving_licence" value="#ctg.Value" />
#ctg.Text
}
and if you wanted to preserve the selected values you will need to set the checked property accordingly:
#foreach (var ctg in Model.DrivingLicenceList)
{
<input type="checkbox" name="person.driving_licence" value="#ctg.Value" #((Model.person.driving_licence ?? Enumerable.Empty<string>()).Contains(ctg.Value) ? "checked=\"checked\"" : "") />
#ctg.Text
}
This being said, we now have a working solution but it is far from anything I would content myself with and stop here. From now on we could start refactoring this mess in order to comply with C# naming conventions (things like property names start with capital letter, ...), introduce real view models (which do not reference domain models), custom HTML helpers that will generate this checkbox lists to avoid writing loops in the views and hardcoding checkboxes, ...

MVC3 Display a dropdown list from one datasource and save to another datasource

I'm getting back to an MVC3 project after a 3 month hiatus. I need to display a drop down list that pulls from Database A, but saves to Database B. The property I need to persist is the NAICS/SIC code. Right now I just provide the user a text box to key in freeform text. So, I have the mechanics of that down. But instead it should provide only a valid list of codes from a source database.
The tricky thing to is I'm using a custom model binder to generate my ViewModels on the fly, so I don't have a distinct .cshtml file to customize.
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")]
public String BusinessTypeCode { get; set; }
Tricky ViewModel
#using Microsoft.Web.Mvc;
#using Tangible.Models;
#model Tangible.Models.WizardViewModel
#{
var currentStep = Model.Steps[Model.CurrentStepIndex];
var progress = ((Double)(Model.CurrentStepIndex) / Model.Steps.Count) * 100;
}
<script type="text/javascript">
$(function () {
$("#progressbar").progressbar({
value: #progress
});
});
</script>
<div id="progressbar" style="height:20px;">
<span style="position:absolute;line-height:1.2em; margin-left:10px;">Step #(Model.CurrentStepIndex + 1) out of #Model.Steps.Count</span>
</div>
#Html.ValidationSummary()
#using (Html.BeginForm())
{
#Html.Serialize("wizard", Model)
#Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
#Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save & Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
#*<input type="submit" value="Save" name="Save" />*#
}
Controller
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard, IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Upload", obj);
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
WizardViewModel
[Serializable]
public class WizardViewModel
{
public String AccountNumber { get; set; }
public int CurrentStepIndex { get; set; }
public Boolean IsInitialized { get { return _isInitialized; } }
public IList<IStepViewModel> Steps { get; set; }
private Boolean _isInitialized = false;
public void Initialize()
{
try
{
Steps = typeof(IStepViewModel)
.Assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t)).Select(t => (IStepViewModel)Activator.CreateInstance(t)).ToList();
_isInitialized = true;
//rewrite this. get the profile and wire them up or something.
this.AccountNumber = Tangible.Profiles.DR405Profile.CurrentUser.TangiblePropertyId;
}
catch (Exception e)
{
_isInitialized = false;
}
}
}
You can specify a template for a specific property on your view model by adding the UIHint attribute to the field. Since your view calls EditorFor on the model it will use the template you specified with UIHint.
BusinessTypeDropdown.ascx - (placed in Views/Shared/EditorTemplates
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% var businessTypes = ViewData["businessTypes"] as IEnumerable<string>; %>
<%= Html.DropDownListFor(m => m , new SelectList(businessTypes, Model))%>
In your View Model
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")][UIHint("BusinessTypeDropdown")]
public String BusinessTypeCode { get; set; }
Then in your controller just set ViewData["businessTypes"] to your list of business types.
Without understanding your "tricky" view model code, it will be hard to make helpful suggestions.
However, there shouldn't be much problem here. You need to somehow create your dropdown list in yoru view, and populate it from data passed from your controller.
All the work happens in your controller. Populate your list or IEnumerable or whatever data source from your first database, then in your post handler save the selection it to your second database (the second part should not be much different from what you already have).

Resources