I'm trying to create a ViewModel in MVC that allows me to switch between the display format and the edit format.
I can get the Controller to select the correct version of the ViewModel and all the properties for the inherited class and base class show in the debugger.
When the ViewModel is passed to the view, only the properties from the inherited class show up.
Can this approach work or should I create two seperate ViewModels?
ViewModel:
public partial class HouseholdViewModel
{
public int Id { get; set; }
public int familyID { get; set; }
public string entityName { get; set; }
public System.DateTime attachmentDate { get; set; }
}
public partial class DisplayHouseholdViewModel : HouseholdViewModel
{
public string phone { get; set; }
}
public partial class CreateHouseholdViewModel : HouseholdViewModel
{
public string familyPhoneCode { get; set; }
public string familyPhone { get; set; }
}
Controller (snippet):
public class HouseholdController : Controller
{
private WhatWorksEntities db = new WhatWorksEntities();
//return viewmodel object
private string displayView = "displayView";
private string createView = "createView";
public IEnumerable<object> GetModel(string view)
{
if (view == displayView)
{
var householdView = (from h in db.tHouseholds
select new DisplayHouseholdViewModel
{
Id = h.householdID,
familyID = h.familyID,
entityName = h.tEntity.entityName,
attachmentDate = h.attachmentDate,
phone = h.familyPhoneCode + " " + h.familyPhone
}).AsEnumerable();
return (householdView);
}
else
{
var householdView = (from h in db.tHouseholds
select new CreateHouseholdViewModel
{
Id = h.householdID,
familyID = h.familyID,
entityName = h.tEntity.entityName,
attachmentDate = h.attachmentDate,
familyPhoneCode = h.familyPhoneCode,
familyPhone = h.familyPhone
}).AsEnumerable();
return (householdView);
}
}
//
// GET: /Household/
public ActionResult Index()
{
var householdView = GetModel(displayView).Cast<DisplayHouseholdViewModel>();
return View(householdView);
}
The view that is returned doesn't display the phone property:
---EDIT to show debugger with phone data---
Debug view:
In your view you need some logic to display the field -
#Html.LabelFor(l => l.Phone, "Phone")
#Html.DisplayFor(p => p.Phone)
And at the top you need to make sure you are displaying the proper view model
#model YourNamespace.ViewModels.DisplayViewModel
If you are trying to change on the same view between two ViewModels you will need to create two separate partial views and toggle their display settings
Related
I am creating a web page that displays 3 different kind of followers (Red, Blue, Yellow) and a Filter form that users can use to filter.
For instance, if the customer selects a Red option from the dropdown list, I wanna show them only the red followers.
I am creating the select part for now, but I am getting an error which reads like this.
The controller for path '/' was not found or does not implement IController.
This is the
AND this is the FilterController:
public class HomeController : Controller
{
private asp6Entities db = new asp6Entities();
public ActionResult Index()
{
var allFlowers = db.FLOWERs.ToList();
List<FLOWER> result = new List<FLOWER>();
foreach (var flower in allFlowers)
{
FLOWER model = new FLOWER();
model = flower;
result.Add(model);
}
return View(result);
}
public ActionResult About()
{
ViewBag.Message = "Our History";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Main Store and Distribution Center.";
return View();
}
[HttpPost]
public ActionResult Index(FilterModel fromColorFilter)
{
string SelectedColor = (fromColorFilter.ColorSelected);
var allFlowers = db.FLOWERs.ToList();
List<FLOWER> result = new List<FLOWER>();
foreach (var flower in allFlowers)
{
if (flower.COLOR.COLOR_NAME == SelectedColor)
{
FLOWER model = new FLOWER();
model = flower;
result.Add(model);
}
}
return View(result);
}
}
THis is the Filter Controller:
public class FilterController : Controller
{
// GET: FilterModel
private asp6Entities db = new asp6Entities();
public ActionResult Index()
{
FilterModel model = new FilterModel();
var color = db.COLORs.ToList().Select(s => new SelectListItem
{
Text = s.COLOR_NAME,
Value = s.COLOR_ID.ToString()
});
return PartialView("~/Views/Shared/_FilterForm.cshtml", new FilterModel { AllColorOptions = color});
}
}
And This is the FilterMethod :
public class FilterModel
{
//declaring the colors selection
public string ColorSelected { get; set; }
//Creating the Size selection
public string SizeSelected { get; set; }
//Creating the starting price selection
public int StartingPriceSelection { get; set; }
//Creating Ends price Selection
public int EndingPriceSelection { get; set; }
//creating IEnumerable of all color options
public IEnumerable<SelectListItem> AllColorOptions { get; set; }
//creating IEnumerable of all Size Options
public IEnumerable<SelectListItem> AllSizeOptions { get; set; }
//creating IEnumerable of Starting Price Options
public IEnumerable<SelectListItem> AllStartingPriceOptions { get; set; }
//creating IEnumerable of Ending Price Options
public IEnumerable<SelectListItem> AllEndingPriceOptions { get; set; }
}
This is the Home Index:
In this Home Index
#Html.Action("Index","FilterForm");
yes this is the best solution
#Html.Action("Index","Filter");
You should change the razor call to this
#Html.Action("Index","Filter");
Your controller is FilterController, therefore, you should use "Filter" as the second parameter, not "FilterForm"
Am using controller rendering, I created one model called Footer.cs and it has below properties.
[SitecoreType(TemplateId = "{1044CFB5-2B85-4A8D-9DCC-34764D2AF5B3}", AutoMap = true)]
public class Footer
{
public virtual Item Item { get; set; }
[SitecoreField(FieldName ="Copyright Text First",FieldType = SitecoreFieldType.SingleLineText)]
public virtual string CopyrightTextFirst { get; set; }
[SitecoreField(FieldName ="Copyright Text Last",FieldType = SitecoreFieldType.SingleLineText)]
public virtual string CopyrightTextLast { get; set; }
}
In My Controller:
public ActionResult FooterTemplate()
{
ISitecoreContext ctx = new SitecoreContext();
var model = ctx.GetCurrentItem<Footer>();
return View(model);
}
But, always getting null result, please help me any one.
You can use:
public ActionResult FooterTemplate()
{
ISitecoreContext ctx = new SitecoreContext();
var model = ctx.GetCurrentItem<Footer>(RenderingContext.Current.Rendering.DataSource);
return View(model);
}
I am trying to include a list of Clients in a drop down box. I am including this list in a form (the Html.BeginForm()) so that I can pass the selected value to my POST controller. I think I am missing something, I have the following classes:
my Invoice ViewModel:
public class InvoiceViewModel
{
public InvoiceViewModel()
{
// makes sure InvoiceItems is not null after construction
InvoiceItems = new List<PrelimInvoice>();
}
public List<PrelimInvoice> InvoiceItems { get; set; }
public List<Client> ClientId { get; set; }
public Client Client { get; set; }
public decimal InvoiceTotal { get; set; }
}
My Client Model:
public class Client
{
public string ClientId { get; set; }
public string Name { get; set; }
}
My SaveInvoice method:
public ActionResult SaveInvoice()
{
var invoice = new Invoice();
TryUpdateModel(invoice);
try
{
invoice.ClientId = User.Identity.Name;
invoice.DateCreated = DateTime.Now;
//Save invoice
proent.Invoices.Add(invoice);
proent.SaveChanges();
//Process the invoice
var preliminvoice = InvoiceLogic.GetInvoice(this.HttpContext);
preliminvoice.CreateInvoice(invoice);
return RedirectToAction("Complete", new { id = invoice.InvoiceId });
}
catch
{
//Invalid - redisplay with errors
return View(invoice);
}
}
And my Index.cshtml is strongly typed to the InvoiceViewModel class.
Index.cshtml is where I generate the form.
I am not sure of the code for creating the Html.DropDownList, and whether or not I need to include a List or something of my Clients. I have dropdownlists in other places but they are strongly typed to models, not viewmodels, hence my confusion.
Can anyone assist me?
Start by adding to your ViewModel the following 2 properties:
SelectedClientId: which stores the selected value
ClientItems: stores the collection of SelectListItems which populates your drop down.
E.G.
public class ClientViewModel
{
public List<Client> Clients;
public int SelectedClientId { get; set; } // from point 1 above
public IEnumerable<SelectListItem> ClientItems // point 2 above
{
get { return new SelectList(Clients, "Id", "Name");}
}
}
Then on your View index.cshtml you would add the following:
#model ClientViewModel
#Html.DropDownListFor(m => m.SelectedClientId, Model.ClientItems)
I've been looking into view models for mvc and I'm looking for the best way to do them. I've read loads of different articles but none seem to be clear as the "best way." So far example I might have a Customer model with the following properties:
First Name
Last Name
Title
Location
Where location is a foreign key to a location table in the database.
I want to be able to edit this customer but only the first name, last name and location. I'm not bothered about the title in the edit. So in my view I will need to pass a customer and a selected list.
Now from what I've read I have the following options (there's probably many more).
So my question is basically which is the best one?
1)
Add a select list to the ViewData["Location"] and just create a strongly typed view of customer?
2)
Create a view model where I pass a customer and select list (the data access is done in the controller):
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, SelectList locations)
{
Customer = customer;
Locations = locations;
}
}
3)
Create a view model where I pass a customer and list of locations and create the select list in the view model.
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, List<Location> locations, string selectedLocation)
{
Customer = customer;
Locations = new SelectList(locations, "LocationID", "LocationName", selectedLocation);
}
}
4)
Pass a customer and repository and do the data access in the view model.
public class ViewModelTest
{
public Customer Customer { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, IRepository repository, string selectedLocation)
{
Customer = customer;
Locations = new SelectList(repository.GetLocations(), "LocationID", "LocationName", selectedLocation);
}
}
5)
Create the view model with just the properties I need:
public class ViewModelTest
{
public string FirstName { get; set; }
public string LastName { get; set; }
public SelectList Locations { get; set; }
public ViewModelTest(Customer customer, SelectList locations)
{
FirstName = customer.FirstName;
LastName = customer.LastName ;
Locations = locations;
}
}
6)
Or some other combination of the above or another way.
All opinions welcome.
Here's what I may suggest: have a view model which reflects the fields of strongly typed view:
public class SomeViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Location { get; set; }
public IEnumerable<SelectListItem> PossibleLocations { get; set; }
}
And in your controller action populate this view model:
public ActionResult Index()
{
var customer = Repository.GetCustomer();
var locations = Repository.GetLocations();
var viewModel = new SomeViewModel
{
FirstName = customer.FirstName,
LastName = customer.LastName,
Location = customer.Location,
PossibleLocations = new SelectList(locations, "LocationID", "LocationName", customer.Location);
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(SomeViewModel viewModel)
{
// TODO: Handle the form submission
return View(viewModel);
}
Of course doing the mapping between the model and the view model manually as shown my example could become quite cumbersome and in this case I would recommend you looking at AutoMapper.
I'd have my ViewModel as this
public class SomeViewModel
{
public Customer Customer { get; set; }
public IEnumerable<Location> PossibleLocations { get; set; }
}
My controller like this:
public ActionResult Index()
{
var viewModel = new SomeViewModel
{
Customer = Repository.GetCustomer(),
PossibleLocations = Repository.GetLocations()
};
return View(viewModel);
}
and then you can access everything in your Customer object in the view like this:
Customer name - <%: Model.Customer.FirstName %> <%: Model.Customer.LastName %>
Location - <%: Html.DropDownList("LocationID", new SelectList(Model.PossibleLocations as IEnumerable, "LocationID", "LocationName", Model.Location.LocationID))%>
I have a simple data model of two tables, email and recipients, email can be sent to one or more recipients
I have setup the database with the two tables, created the Linq to SQL repository, built the controllers and the strongly typed view.
This works fine when I want to select all records from the database
public IList<AllMailDetail> ListAll()
{
var allMail =
from m in _datacontext.mail_receiveds
join r in _datacontext.mail_recipients on m.DeliveryId equals r.DeliveryId
select new AllMailDetail {
DeliveryId = m.DeliveryId,
MessageId = m.MessageId,
SentFrom = m.SentFrom,
FilePath = m.FilePath,
FileName = m.FileName,
SentDateTime = m.SentDateTime,
ReceivedDateTime = m.ReceivedDateTime,
Subject = m.Subject,
SpamScore = m.SpamScore,
IsSpam = m.IsSpam,
SenderIP = m.SenderIP,
Header = m.Header,
SentTo = r.SentTo
};
return allMail.ToList <AllMailDetail>();
}
The custom type class
public class AllMailDetail
{
public int DeliveryId { get; set; }
public int? MessageId { get; set; }
public string SentFrom { get; set; }
public string FilePath { get; set; }
public string FileName { get; set; }
public string SentDateTime { get; set; }
public DateTime ReceivedDateTime { get; set; }
public string Subject { get; set; }
public byte? SpamScore { get; set; }
public bool? IsSpam { get; set; }
public string SenderIP { get; set; }
public string Header { get; set; }
public string SentTo { get; set; }
}
The controller simply sends the contents from the repository to the strongly typed view
public ActionResult Index()
{
return View(_repository.ListAll());
}
To get just one mail record from the database I have the following code that accepts a deliveryId
public IQueryable<AllMailDetail> GetMail(int? id)
{
var allMail =
from m in _datacontext.mail_receiveds
join r in _datacontext.mail_recipients
on m.DeliveryId equals r.DeliveryId
where m.DeliveryId == id
select new AllMailDetail
{
DeliveryId = m.DeliveryId,
MessageId = m.MessageId,
SentFrom = m.SentFrom,
FilePath = m.FilePath,
FileName = m.FileName,
SentDateTime = m.SentDateTime,
ReceivedDateTime = m.ReceivedDateTime,
Subject = m.Subject,
SpamScore = m.SpamScore,
IsSpam = m.IsSpam,
SenderIP = m.SenderIP,
Header = m.Header,
SentTo = r.SentTo
};
return allMail;
}
And its controller code
public ActionResult Details(int? id)
{
var mail = _repository.GetMail(id);
if (mail == null)
return View("NotFound");
return View(mail);
}
I had been trying to display the output for a single record by also using a strongly typed view having Inherits="System.Web.Mvc.ViewPage At the top of the aspx page but I got the following error
The model item passed into the dictionary is of type 'System.Data.Linq.DataQuery`1[projectMail.Models.AllMailDetail]' but this dictionary requires a model item of type projectMail.Models.AllMailDetail'.
I fixed this error after much searching and found this post most helpful
MVC LINQ to SQL Table Join Record Display
so my view is no longer strongly typed and I build the page as follows
<% foreach (projectMail.Models.AllMailDetail item in (IEnumerable)ViewData.Model)
{ %>
...items...
<% } %>
This works fine, but it seems the long way round. The thing I can’t figure out is
Why does the second query need to be IQueryable
Why didn’t it work when the view was strongly typed
How can it be made to work with a strongly typed view
Is this the best way of dealing with joins in MVC using LINQ to SQL
Hmmmm, Try in the controller
return View(_repository.GetMail( id).SingleOrDefault());
You're trying to bind an IQueryable datasource to an AllMailDetail View, the above should fix you.