ASP.Net MVC Passing multiple parameters to a view - asp.net-mvc

In ASP.Net MVC I would like to render a different partial view depending on the renderview query string parameter.
Therefore providing the facility for the user to choose to view products by thumbnail or by details.
I have access to the chosen parameter in the controller but I do not know how to or, if I should be passing this to the view along with the products list so the view can implement the logic for deciding which partial view to display?
public ActionResult Products(string id, int? renderview)
{
var products = productRepository.GetProducts(id).ToList();
return View("Products", products);
}
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MLBWebRole.Models.Product>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Products
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Products</h2>
<p>This is the Products page</p>
<p>thumbnails details</p>
<% if (renderview == 1)
{%>
<% Html.RenderPartial("ProductsDetailList"); %>
<% }
else
{ %>
<% Html.RenderPartial("ProductsThumbnailList"); %>
<% } %>
</asp:Content>

Your View Should be something like:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.MyModel>" %>
Then in MyModel
Expose Property:
public bool RenderDetailView {get;set;}
In your controller action:
public ActionResult Products(string id, int? renderview)
{
var products = productRepository.GetProducts(id).ToList();
return View("Products", new MyModel {RenderDetailView = renderview.HasValue});
}
Then in your view, make check like:
<% if (Model.RenderDetailView)
Ideally, all the properties or parameters or data which a View needs in order to present itself should be part of Model.
I hope it helps.

An alternative approach would be to use Restful Urls to invoke the appropriate controller action and view.
This makes the urls reflect what you are seeing on the screen and makes the design more extensible; should you need to add other views of the data in the future (summary, latest, etc) you add the new view, no need for partials unless the main body of the view gets more complicated and has to be factored out to a partial view.
The URLs would look like:
~/product/1/detail
~/product/1/thumbnail
And correspond to ProductController methods:
public ActionResult Detail(String id)
{
var products = productRepository.GetProducts(id).ToList();
return View("Detail", products);
}
public ActionResult Thumbnail(string id)
{
var products = productRepository.GetProducts(id).ToList();
return View("Thumbnail", products);
}
You enable the routing with a route like:
{controller}/{id}/{action}

Paul's method is good, but if you decide you want to pass the int, you need to create a view model.
In your controller add this
public class ProductsFormViewModel
{
// Properties
public Products Products { get; private set; }
public int? Renderview { get; private set; }
// Constructor
public ProductsFormViewModel(Products p_products, int? p_renderView)
{
Products = p_products;
Renderview = renderView;
}
}
Then pass this into the view
return View(new ProductsFormViewModel(products, renderview);
And then in the view
Inherits="System.Web.Mvc.ViewPage<YourNamespace.Controllers.ProductsFormViewModel>"

Related

How to fetch one row from db in asp mvc

ASP.net mvc in new for me, for some time I used php (no mvc), but now i'm interested, how I can fetch one row from db? without foreach, for example in title...
here is some code:
controller
public ActionResult Index()
{
var pages = (from page in db.Pages where page.PageName == "index" select page).ToList();
return View(pages);
}
view:
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<% foreach (var item in Model)
{ %>
<%= Html.Encode(item.Text) %>
<% }
%>
</asp:Content>
In your controller, instead of .ToList() you can use the .FirstOrDefault() method, this will return only the first row from the database.
Then in your view you won't need the foreach.
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<%= Html.Encode(item.Model.Text) %>
</asp:Content>
What you're doing there is creating a List datatype variable and passing it in as the Model to your view. Assuming this is the only piece of data your page needs. Here's what you would do;
public ActionResult Index()
{
string page = db.pages.where(p => p.PageName == "index").FirstOrDefault().PageName;
return View(page);
}
There in your page, Model will now be that single string value and you can do this;
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<%= Model %>
</asp:Content>
Although it is best practice to create a ViewModel for the page with the just the properties your page will need and pass that in as the Model.
public class MypageViewModel
{
public string PageName { get; set; }
}
Then do this in the controller
public ActionResult Index()
{
MypageViewModel MyModel = new MypageViewModel();
MyModel.PageName = db.pages.where(p => p.PageName == "index").FirstOrDefault().PageName;
return View(MypageViewModel);
}
Hope that helps.
If you are using Entity Framework:
var singleItem = db.pages.Find(id);
This will use the Primary Key of your entity.
If you have a composite primary key consisting of multiple properties, Find will still work (because it can take multiple values):
var singleItem = db.pages.Find(key1, key2);

ASP.NET Error: "Unable to cast object of type '<>f__AnonymousType1`2"

I very new to .NET and Entity Framework, and I have a problem with my code (below). I am getting the following error:
Unable to cast object of type '<>f__AnonymousType1`2[
SamWinInterface.Models.tbl_interface_category,
SamWinInterface.Models.tbl_interface_menu]' to type
'SamWinInterface.Models.tbl_interface_menu'.
This is my code:
public ActionResult Index(int id=-1)
{
ViewBag.Menus = from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category on
menu.fld_category_id equals cat.id where
cat.fld_customer_id == id select new { cat, menu };
return View();
}
I'm trying to get menus depending on which category is chosen.
Something like:
<% foreach (tbl_interface_menu m in (IEnumerable)ViewBag.Menus)
{ %>
<%= m.fld_section2_title %>
<% } %>
but I'm getting the above error. How can I get the menus?
You cannot pass anonymous objects to views. This doesn't work because anonymous types are emitted as internal. And because ASP.NET views are compiled into a separate assembly at runtime they cannot access those times because they reside in a different assembly. This basically means that an anonymous object that you have defined in your controller action cannot be accessed in your view.
So as always in an ASP.NET MVC application start by defining view a model:
public class MyViewModel
{
public Category Category { get; set; }
public Menu Menu { get; set; }
}
then have your controller action fill this view model and pass it to the view:
public ActionResult Index(int id=-1)
{
var model =
from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category
on menu.fld_category_id equals cat.id
where cat.fld_customer_id == id
select new MyViewModel { Category = cat, Menu = menu };
return View(model);
}
and finally have a strongly typed view:
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<AppName.Models.MyViewModel>>"
%>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var item in Model) { %>
<%= item.Menu.fld_section2_title %>
<% } %>
</asp:Content>
As Darin said, you cannot pass anonymous types to views, but you could convert them to Expando objects, and that would prevent you from having to define viewmodels.
Personally I would probably just define viewmodels, but this option is handy in a pinch.

ASP.Net MVC Model problems

Can you please help me understand what is the issue with generic collection? Thanks in advance!
Error: The model item passed into the dictionary is of type 'System.Collections.Generic.List1[DomainModel.Product]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[DomainModel.Entities.Product]'.
MODEL:
namespace DomainModel.Concrete
{
public class SqlProductsRepository : IProductsRepository
{
private Table<Product> productsTable;
public SqlProductsRepository(string connString)
{
productsTable = (new ProaductDataContext(connString)).GetTable<Product>();
}
public IQueryable<Product> Products
{
get { return productsTable; }
}
}
}
INTERFACE
namespace DomainModel.Abstract
{
public interface IProductsRepository
{
IQueryable<Product> Products { get; }
}
}
VIEW
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ViewMaster.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<DomainModel.Entities.Product>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Products
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var product in Model)
{ %>
<div class = "item">
<h3> <%=product.Name%></h3>
<%= product.Description%>
<h4><%= product.Price.ToString("c")%></h4>
</div>
<%} %>
</asp:Content>
The error message tells you all your need to know;
The model item passed into the dictionary is of type
'System.Collections.Generic.List1[DomainModel.Product]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[DomainModel.Entities.Product]'.
If you look at your view you can see
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ViewMaster.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<DomainModel.Entities.Product>>" %>
The Inherits="System.Web.Mvc.ViewPage<IEnumerable<DomainModel.Entities.Product>>" is what's tripping you up. You're passing in an IEnumerable of DomainModel.Product, but you're expecting something else. It's a bit strange you have two classes named the same within close to the same namespace, but never mind, you need to ensure you're using the same class, in the same namespace in both the controller and the view.
So I'd try changing your view to become
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ViewMaster.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<DomainModel.Product>>" %>
Then try to figure out why you have two Product classes :)

ASP.net MVC2 Passing data between strongly typed views

I have the following MVC2 view that is strongly typed with a viewmodel, the viewmodel contains a list of values from one db table, I need to display a single value from a second table in the view, this is my view code
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<CustomerDatabase.WebUI.Models.CustomerSitesListViewModel> " %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Customer Sites
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var customerSite in Model.CustomerSites) { %>
<% Html.RenderPartial("CustomerSiteSummary", customerSite); %>
<%} %>
</asp:Content>
This is the viewmodel, notice i am including a Customer member in the viewmodel as i want to display the customer name in addition to the list of customer sites
namespace CustomerDatabase.WebUI.Models
{
public class CustomerSitesListViewModel
{
public IList<CustomerSite> CustomerSites { get; set; }
public PagingInfo PagingInfo { get; set; }
public Customer customer { get; set; }
}
}
This is my controller code for the view
public ViewResult List([DefaultValue(1)] int page)
{
var customerSitesToShow = customerSiteRepository.CustomerSites;
var viewModel = new CustomerSitesListViewModel
{
CustomerSites = customerSitesToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = customerSitesToShow.Count()
}
};
return View(viewModel); //Passed to view as ViewData.Model (or simply model)
}
This is my partial view that renders the list,
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CustomerDatabase.Domain.Entities.CustomerSite>" %>
<div class="item">
<div class="customer-list-item">
<h2><%:Model.customer.CustomerName%></h2>
<%: Model.AddressLine1 %>
<%: Model.AddressLine2%>
Although intellisense lets me access the customer object from the view with
<h2><%:Model.customer.CustomerName%></h2>
An error is thrown when i navigate to the view,
Object reference not set to an instance of an object.
Source Error:
Line 7: <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
Line 8: <% foreach (var customerSite in Model.CustomerSites) { %>
Line 9: <%:Model.customer.CustomerName%>
Line 10: <% Html.RenderPartial("CustomerSiteSummary", customerSite); %>
Line 11: <%}
I think the error is due to the view rendering a list, i tried changing the viewmodel member to
public IList<Customer> {get; set;}
but this doesn't work either.
Can anyone suggest a way i can achieve this or offer any advice on where i am going wrong this is one problem i haven't been able to resolve after hours or researching on the Internet?
It looks like one of the model properties are not initialized. If you add a breakpoint on that line and check the variables I'm pretty sure you'll find 1 that is null.

Asp.net mvc, view with multiple updatable parts - how?

I have started doing asp.net mvc programming and like it more everyday.
Most of the examples I have seen use separate views for viewing and editing details of a specific entity.
E.g. - table of music albums linking to separate 'detail' and 'update' views
[Action] | Title | Artist
detail, update | Uuuh Baby | Barry White
detail, update | Mr Mojo | Barry White
With mvc how can I achieve a design where the R and the U (CRUD) are represented in a single view, and furthermore where the user can edit separate parts of the view, thus limiting the amount of data the user can edit before saving?
Example mockup - editing album detials:
I have achieved such a design with ajax calls, but Im curious how to do this without ajax.
Parts of my own take on this can be seen below. I use a flag (enum EditCode)
indicating which part of the view, if any, that has to render a form. Is such a design in accordance with the framework, could it be done more elegantly?
AlbumController
public class AlbumController : Controller
{
public ActionResult Index()
{
var albumDetails = from ManageVM in state.AlbumState.ToList()
select ManageVM.Value.Detail;
return View(albumDetails);
}
public ActionResult Manage(int albumId, EditCode editCode)
{
(state.AlbumState[albumId] as ManageVM).EditCode = (EditCode)editCode;
ViewData["albumId"] = albumId;
return View(state.AlbumState[albumId]);
}
[HttpGet]
public ActionResult Edit(int albumId, EditCode editCode)
{
return RedirectToAction("Manage", new { albumId = albumId, editCode = editCode });
}
// edit album details
[HttpPost]
public ActionResult EditDetail(int albumId, Detail details)
{
(state.AlbumState[albumId] as ManageVM).Detail = details;
return RedirectToAction("Manage", new { albumId = albumId, editCode = EditCode.NoEdit });// zero being standard
}
// edit album thought
[HttpPost]
public ActionResult EditThoughts(int albumId, List<Thought> thoughts)
{
(state.AlbumState[albumId] as ManageVM).Thoughts = thoughts;
return RedirectToAction("Manage", new { albumId = albumId, editCode = EditCode.NoEdit });// zero being standard
}
Flag - EditCode
public enum EditCode
{
NoEdit,
Details,
Genres,
Thoughts
}
Mangae view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Controllers.ManageVM>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Manage
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Manage</h2>
<% if(Model.EditCode == MvcApplication1.Controllers.EditCode.Details)
{%>
<% Html.RenderPartial("_EditDetails", Model.Detail); %>
<% }else{%>
<% Html.RenderPartial("_ShowDetails", Model.Detail); %>
<% } %>
<hr />
<% if(Model.EditCode == MvcApplication1.Controllers.EditCode.Thoughts)
{%>
<% Html.RenderPartial("_EditThoughts", Model.Thoughts); %>
<% }else{%>
<% Html.RenderPartial("_ShowThoughts", Model.Thoughts); %>
<% } %>
The last part feels messy to me. I would recommend wrapping those with Html Helpers to clean up your view.
<h2>Manage</h2>
<% Html.RenderDetailsPartial(Model.EditCode) %>
<hr />
<% Html.RenderThoughtsPartial(Model.EditCode) %>
Let the HTMLHelper determine which view to use based on the EditCode.

Resources