Passing a model object through ViewBag - asp.net-mvc-3

I was wondering if it is possible to pass a model object through ViewBag. I tried the following codes but somehow in my View, it is only the path of the model which is displayed.
Controller:
public ActionResult Tempo()
{
DateTime date1 = new DateTime(1990, 1, 1);
Employee emp = new Employee(3, "fara", "hass", date1, 56.6m, 0);
ViewBag.EmployeeId = emp.EmployeeID;
ViewBag.Fname = emp.Fname;
ViewBag.Employee = emp;
}
View:
#{
ViewBag.Title = "Tempo";
}
#model LayoutProject.Models.Employee
<h2>Tempo</h2>
<div>
<h4>ViewBag</h4>
<br />
EmployeeID: #ViewBag.EmployeeId
<br />
Fname: #ViewBag.Fname
<br />
Employee : #ViewBag.Employee
</div>
The Fname and EmployeeId is correctly displayed but not the Employee itself. Am I missing something here or it is simply not possible to pass a model through ViewBag?

It would be better to create a view model to pass to the view:
public class TempoViewModel
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { private get; set; }
public DateTime EmployeeStartDate { private get; set; }
//any other properties here that make up EmployeeInformation
public string EmployeeInformation
{
get
{
//Format your employee information in the way you intend for the view
return string.Format("Employee: {0}, {1}, {2}", this.FirstName, this.LastName, this.EmployeeStartDate);
}
}
}
Then have a controller create the view model:
public ViewResult Tempo()
{
employee = //logic to retrieve employee information
//map model to viewmodel
var viewModel = new TempoViewModel()
{
EmployeeId = employee.EmployeeID,
FirstName = employee.Fname,
LastName = employee.Lname, //or set whatever properties are required to display EmployeeInformation
EmployeeStartDate = employee.StartDate,
};
return View(viewModel);
}
And then display the view model in the view:
#model TempoViewModel
#{
ViewBag.Title = "Tempo";
}
<h2>Tempo</h2>
<div>
<h4>Tempo Employee Information</h4>
<br />
EmployeeID: #Model.EmployeeId #* Do you really want to display the employee id? *#
<br />
Fname: #Model.FirstName
<br />
Employee: #Model.EmployeeInformation
</div>
Update:
With your current implementation, what you are trying to achieve when you call #ViewBag.Employee in the view, is to write the model out as a string representation. With the current implementation, to convert the model to a string, the ToString() method of the model is called. As you (probably) have not overridden the ToString() method, the inherited object implementation is called instead which writes out the complete namespace and class name (which is what I assume you mean when you say path).
To correct your current solution, you can add an implementation of ToString() to your Employee class. For example:
public class Employee
{
public Employee(int employeeId, string firstName, string lastName)
{
this.EmployeeId = employeeId;
this.FName = firstName;
this.LName = lastName;
//etc
}
public int EmployeeId { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public override string ToString()
{
//Implement your required string representation of the object here
return string.Format("EmployeeId: {0}, FName: {1}, LName: {2}", EmployeeId, FName, LName);
}
}

Related

ASP.NET Core MVC Entity Framework binding for sub entities

Let's suppose I have these 2 entities:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id_person {get;set;}
[Column(TypeName = "varchar(255)")]
[StringLength(255)]
public String name {get;set;}
}
public class InterestCenter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id_interest {get;set;}
[Column(TypeName = "varchar(255)")]
[StringLength(255)]
public String name {get;set;}
}
I want to set a many to many relationship between this 2 entities. This mean a Person can have many interest centers.
Here is what I've done:
public class PersonHasInterestCenter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id {get;set;}
[ForeignKey("person")]
public long id_person {get;set;}
public virtual Person person { get; set; }
[ForeignKey("interestcenter")]
public long id_interest {get;set;}
public virtual InterestCenter interestcenter { get; set; }
}
Now I want to create a controller action and cshtml razor view in order to edit and save a Person. What I want to do is to display a set of checkboxes with all available interst centers.
Here is what I've done:
[HttpPost]
public async Task<IActionResult> MyAction(long id, [Bind("id_person,name")] Person p)
{
ViewBag.interestcenters = mydbcontext.interestcenters;
ViewBag.message = "";
if (p.name == "")
{
ViewBag.message = "You need to set name.";
}
else if (ModelState.IsValid == false)
{
ViewBag.message = string.Join("; ", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage));
}
else
{
mydb_context.Update(p);
await mydb_context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(p);
}
And here is the associated cshtml razor view:
#model myproject.Person
<form asp-action="MyAction">
<div>#ViewBag.message</div>
<input type="hidden" asp-for="id_person" />
<input asp-for="name" />
#foreach (var name in ViewBag.interestcenters)
{
<input type="checkbox" asp-for="WHAT_SHOULD_I_PUT_THERE" />#item.name
}
<input type="submit">
</form>
Everything works great for create or update person's name but I have a problem with interest center checkboxes. I have also tried to create a view model. But I do not manage to make it work...
Thanks for your help
The simple answer is ... use viewmodels whenever have the chance.
In order to bind those interests you could create something similar to:
public class MyViewModel
{
public long UserId { get; set; }
public List<InterestCenterViewModel> InterestCenters { get; set; }
}
public class InterestCenterViewModel
{
public int Id { get; set; }
public bool IsSelected { get; set; }
public string Name { get; set; }
}
On the get method:
[HttpGet]
public async Task<IActionResult> MyAction(long id)
{
var _InterestCenters = mydbcontext.interestcenters;
// Create your vm here
var model = new MyViewModel
{
UserId = id,
InterestCenters = _InterestCenters.Select(p => new InterestCenterViewModel
{
Id = p.Id,
Name = p.Name
IsSelected = false
}).ToList()
}
return View(model);
}
On your post:
[HttpPost]
public async Task<IActionResult> MyAction(MyViewModel model)
{
// Something was not filled or did not match your requirements
if (!ViewState.IsValid)
{
return View(model);
}
// All good. To your stff here
return Ok();
}
So all you need is to pass the list of interests created using the above model to the view:
<input asp-for="UserId" type="hidden" />
#for(int i = 0; i < MyViewModel.InterestCenters.Count; i++)
{
<input type="checkbox" asp-for="MyViewModel.InterestCenters[i].IsSelected" />#MyViewModel.InterestCenters[i].Name
}
When working with lists you need to use for instead of foreach. The html generated is using that index i instead of the name which is the way to make the difference between items.
Here is a link describing how viewmodels and asp.net works: Microsoft official documentation

MVC Editor Templates

I was trying nested Editor Templates but couldnt get it working. The structure of my model is
public class CompanyModel
{
public EmployeeModel Emp { get; set; }
}
public class EmployeeModel
{
public EmpType EmployeeType { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public string EmailAddress { get; set; }
}
public enum EmpType
{
Manager = 1,
Assistant = 2,
TeamLeader = 3
}
Each EmpType will have different EditorTemplates
The Index view is
#model CompanyModel
<h3>Index</h3>
#Html.EditorFor(m => m.Emp,"Employee")
Then i have create EditorTemplates(Employee.cshtml)
#model EmployeeModel
<h2>Employee</h2>
#Html.EditorForModel("EmpType_"+ Model.EmployeeType.ToString())
EditorTemplate(EmpType_Manager.cshtml)
#model EmployeeModel
<h2>EmpType_Manager</h2>
I am Manager
For testing i am populating the model with dummy data in the controller
public ActionResult Index(CompanyModel model)
{
EmployeeModel emp = new EmployeeModel
{
EmployeeType = EmpType.Manager,
Name = "xxxx",
StartDate = DateTime.Now,
EmailAddress = "xxx#yyy.com"
};
model = new CompanyModel();
model.Emp = emp;
return View(model);
}
When i run this it does not call the EmpType_Manager template. Can some one help me on this. I have tried using partial view which works. But would like to use EditorTemplate instead of Partial view.
Well looks like this is not possible as it is by mvc design.

MVC 3 Selectlist/DropDownList Is Not Updating My Model

I hope someone can help with this one. I have three Model classes like this:
public class Provider
{
public Guid ProviderId { get; set; }
public string Name { get; set; }
public Guid LocationId { get; set; }
public virtual Location Location { get; set; }
}
public class Location
{
public Guid LocationId { get; set; }
public string NameOrCode { get; set; }
public string Description { get; set; }
public string StreetNumber { get; set; }
public string StreetAddress1 { get; set; }
public string StreetAddress2 { get; set; }
public string City { get; set; }
public int? StateId { get; set; }
public string Zip { get; set; }
public string ContactPhone { get; set; }
public virtual State State { get; set; }
}
public class State
{
public int StateId { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
As you can see, a Provider has a Location (separate class for reuse elsewhere), and a Location has a State (which is null until selected).
My Controller looks like this for my Create methods:
public class ProviderController : BaseController
{
private SetupContext db = new SetupContext();
// other CRUD methods ...
//
// GET: /Provider/Create
public ActionResult Create()
{
Location location = new Location()
{
LocationId = Guid.NewGuid(),
NameOrCode = Resources.BillingLocation,
Description = Resources.BillingLocationDescription
};
Provider provider = new Provider()
{
ProviderId = Guid.NewGuid(),
LocationId = location.LocationId,
Location = location
};
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
return View(provider);
}
//
// POST: /Provider/Create
[HttpPost]
public ActionResult Create(Provider provider)
{
if (ModelState.IsValid)
{
db.Locations.Add(provider.Location);
db.Providers.Add(provider);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
return View(provider);
}
// other CRUD methods ...
}
Finally, my View looks like this:
<div class="editor-label">
#Html.LabelFor(model => model.Location.StateId, #Resources.Location_State_Display_Name)
</div>
<div class="editor-field">
#Html.DropDownList("StateId", #Resources.ChooseFromSelectPrompt)
#Html.ValidationMessageFor(model => model.Location.StateId)
</div>
My problem is that the state the user selects in the DropDownList never gets set on my Model on the Create POST. I have similar code in my Edit View and the state is populated correctly in that View (that is, the state associated with an existing Provider.Location shows selected in the DropDownList for the user to edit if desire), but in both the Create and the Edit Views the selection made by the user is never registered in my Model (specifically the Provider.Location.StateId) coming in from the POST.
Looking at the HTML produced I see this:
<div class="editor-label">
<label for="Location_StateId">State/Territory</label>
</div>
<div class="editor-field">
<select id="StateId" name="StateId"><option value="">[Choose]</option>
<option value="1">Alabama</option>
<option value="2">Alaska</option>
<!-- more options ... -->
</select>
<span class="field-validation-valid" data-valmsg-for="Location.StateId" data-valmsg-replace="true"></span>
</div>
I suspect I need to somehow convey the Location.StateId relationship instead of just StateId as I see above but I can't figure out the correct syntax to do that. I've tried changing my ViewBag dynamic property to Location_StateId like this:
ViewBag.Location_StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
And the DropDownList in my View like this:
#Html.DropDownList("Location_StateId", #Resources.ChooseFromSelectPrompt)
I figured then perhaps that notation would work because the label beside my DropDownList was rendered as:
<div class="editor-label">
<label for="Location_StateId">State/Territory</label>
</div>
This attempt did not work. Can you help me out?
Thanks in advance.
#Html.DropDownList("Location.StateId", #Resources.ChooseFromSelectPrompt)
Also the following line doesn't do anything useful:
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
You are assigning a SelectList to something that is supposed to be a scalar property. You probably wanted to pass the collection as ViewBag:
ViewBag.States = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
and then in the view:
#Html.DropDownList("Location.StateId", (SelectList)ViewBag.States)

Get Id and Type from Html.DropDownList to Controller

I have a class called
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Country Country { get; set; }
}
public class Country
{
public int Id { get; set; }
public string Type { get; set; }
}
My MVC View Page is Strongly typed to Person and there is a dropdownlist which show the list of countries.
In My Controller Index
public ActionResult Index()
{
LoadCountryList();
return View(Person);
}
private void LoadCountryList()
{
IEnumerable<CountryList> countryList = CountryListService.GetCountryList();
ViewData["CountryList"] = new SelectList(country, "Id", "Type", 0);
}
Code in the html
<%: Html.DropDownListFor(model => model.Country.Id, (IEnumerable<SelectListItem>)ViewData["CountryList"], "--Select--")%>
When the page is submitted Create method is called in the controller
public ActionResult Create(Person person)
{
// person.Country.Id has the value
// person.Country.Type is null
}
I am getting only the Country Id from the object person in the Create Method. The Country Id is loaded inside the Person Object under Country.
Is there any way I can get both the Id and Type of the country when passed from the Page to the Controller?
I know I am passing Html.DropDownListFor(model => model.Country.Id .... from here.
Is there any Solution so that I get Id and Type in the controller.
Thanks
Passing it through the person object is not the best way to do it. Instead, assign an ID to your dropdown list like this:
<%: Html.DropDownListFor(
model => model.Country.Id,
(IEnumerable<SelectListItem>)ViewData["CountryList"], "--Select--")
new { id = "CountryID" }
%>
and then put that in as a parameter to your Create method:
public ActionResult Create(Person person, int CountryID)
{
var country = CountryListService.GetCountryList().Where(x => x.id == CountryID);
person.Country = country;
...
}
ASP .NET MVC will look for a control that has the same ID name as the parameter in the method call and pass it through.

MVC3 & EF4 How to pass data to a select list using a viewmodel

I have viewmodel which i am contructing in a Create get action,
public class SiteAdminCreateViewModel
{
public int CustomerId { get; set; }
public string ContactName { get; set; }
[Required(ErrorMessage = "A contact number is Required")]
public string ContactNo { get; set; }
public IEnumerable<SelectListItem> CustomerNames { get; set; }
}
public ViewResult Create(SiteAdminCreateViewModel model)
{
var query = from cs in repository.CustomerSites
select new SiteAdminCreateViewModel
{
CustomerId = cs.Customer.CustomerId,
ContactName = cs.ContactName,
ContactNo = cs.ContactNo,
CustomerNames = ??
};
return View(query.ToList());
}
In the viewmodel i have a select list defined as CustomerNames, i want to be able to use that select list to insert the CustomerId value (also defined in the viewmodel) taken from the Customers table which is a seperate but related entity,
Can anyone please help with how i can set the select list up in the controller to receive a list of CustomerNames from the Customer entity?
Also my view throws an exception when i change its declaration to accept the viewmodel,
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[CustomerOrders.WebUI.Models.SiteAdminCreateViewModel]', but this dictionary requires a model item of type 'CustomerOrders.WebUI.Models.SiteAdminCreateViewModel'.
Any help/advice is appreciated.
public ActionResult MyController(SiteAdminCreateViewModel model)
{
var query = from cs in repository.CustomerSites
select new SiteAdminCreateViewModel
{
Value= cs.Customer.CustomerId,
Text= cs.ContactName
};
ViewBag.CustomerNames=query.ToList();
return View(model);
}
inside view
#Html.DropDownListFor("CustomerNames")

Resources