Inline Template, Assign HTML as string to model Property - asp.net-mvc

is there a way in ASP.NET MVC(2) to do something like that:
<table>
<tr>
<% Model.Pager.TemplateNextPage = { %>
<li><a href={link-next-page}>Next</a></li>
<% } %>
<% Model.Pager.TemplatePageNumber = { %>
<li><a href={link-page-number}>{number}</a></li>
<% } %>
<% Model.Pager.TemplatePageNumberActive = { %>
<li>{number}</li>
<% } %>
</tr>
</table>
the general idea is to enable designers to specify templates.

Not like that no. The code betweeen "%>" and "<%" e.g.
<% Model.Pager.TemplateNextPage = { %>
<li><a href={link-next-page}>Next</a></li>
<% } %>
... will translate to ...
Model.Pager.TemplateNextPage = {;
Response.Write("<li><a href={link-next-page}>Next</a></li>");
};
when compiled (ignoring the whitespace in the example). So it does not really work.
What you can do however, is use Asp.Net WebForms controls and create a custom one. It will work within Asp.Net MVC page. Not all features work (e.g. related to form postback), but enough to do what you want.
E.g. a table control is like this:
<asp:Table runat="server">
<asp:TableFooterRow>
<asp:TableCell>foo</asp:TableCell>
<asp:TableCell>faa</asp:TableCell>
</asp:TableFooterRow>
</asp:Table>
You could extend that - or create a control from scratch allowing you to create your own TemplateNextPage etc.
...but this is not Asp.Net MVC way - it is the old Asp.Net WebForms way. It just works within Asp.Net MVC pages.
The other option you have is to create your own template engine, but that's quite a lot of work and I assume the intellisense part is very important to you so it would screw that as well.

Related

asp.net mvc - Update/pass objects with no association between controller and view

Essentially what I have is an entity class (Article) and one article may belong to n categories. This is where one would expect a field for the categories (say an ICollection) but the database is not built that way and I cannot modify the Entity Framework model.
When a category is selected in the view, the user has to select a property (a string list between 1 to 10. It will look like this:
Category Option
===============================
[x] Category 1 |---------|v|
[x] Category 2 |---------|v|
[x] Category 3 |---------|v|
[x] Category 4 |---------|v|
[x] - checkbox
|---|v| - dropdown list
However I'm thinking that perhaps I could create a ViewModel for this particular case. I was thinking something like
namespace Models.ViewModels
{
public class ArticleViewModel
{
public Article Article { get; set; }
public ICollection<Category> Categories { get; set; }
public ArticleViewModel()
{
Categories = new List<Category>();
}
}
}
However when I post the form to the controller the ArticleViewModel and all of its internals are null. I'm not sure why.
The view itself is pretty basic, it will have two separate forms: one for modifying article details and one for assigning categories to the article.
As a guy new to asp.net mvc, I find it a little difficult to wrap my head around this problem.
Which approach would you recommend?
Update: added code for the view.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.ViewModels.ArticleViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Modify article: <%= Model.Article.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Properties</legend>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Title) %>
<%= Html.TextBoxFor(model => Model.Article.Title) %>
<%= Html.ValidationMessageFor(model => Model.Article.Title) %>
</span>
</div>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Type) %>
<%= Html.TextBoxFor(model => Model.Article.Type) %>
<%= Html.ValidationMessageFor(model => Model.Article.Type) %>
</span>
</div>
[continued...]
<div>
<%= Html.HiddenFor(model => Model.Article.Id) %>
<input type="submit" value="Save" />
</div>
</fieldset>
<% } %>
</asp:Content>
Routing looks like this:
routes.MapRoute(
"Articles",
"Articles/{action}/{id}",
new { controller = "Articles", action = "Edit" }
);
The whole point of MVC is that your View is decoupled from your Model. It doesn't matter what your EF entities look like.
Create your view and view model classes the way you want. Don't think about your DB structure. If you need a collection of Categories than create it. This code should represent the 'domain' of UI.
In the controller map your view model to EF and vice versa. It's controllers responsibility to understand data access code (or service layer code) and view code.
This approach allows you to take a full advantage of MVC and decoupling between UI and database. Although you end up writing a bit more code for your models it pays up with interest later on (you can modify your db without touching UI and vice versa).
If you find yourself spending a lot of time copying data from EF entities to models than you might want to take a look at this brilliant library: AutoMapper
When you set up your view like that, each of those fields is going to have the same name attribute and I don't think MVC will know how to map it back to a single item properly. What does your [HttpPost] action look like? If its anything like:
[HttpPost]
public ActionResult Edit( ArticleViewModel model )
{
..
}
then I doubt it will map correctly. You could maybe try accepting List<ArticleViewModel> but I don't think that would work correctly without a CustomModelBinder.
What #Jakub said is correct: de-couple your thinking and design your ViewModel in the most logical way for the view; you can worry about mapping it back to the service layer later. Its a paradigm shift for sure, but one that's worth making. = )

linq groupby in strongly typed MVC View

How do i get an IGrouping result to map to the view?
I have this query:
var groupedManuals = manuals.GroupBy(c => c.Series);
return View(groupedManuals);
What is the proper mapping for the ViewPage declaration?
Inherits="System.Web.Mvc.ViewPage<IEnumerable<ProductManual>>"
I am still in the early stages of learning Linq so I may be wrong but I think that that ViewPage will need to be something like
Inherits="System.Web.Mvc.ViewPage<IEnumerable<IGrouping<Series, Manual>>>
In the instance of helping others out. This is what I was attempting to accomplish.
The action code
var groupedManuals = manuals.GroupBy(c => c.Series);
return View(groupedManuals);
The View
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<IGrouping<string, ProductManualModel>>>" %>
<% foreach (var item in Model)
{ %>
<div class="manual-category">
<h5>
<%= Html.Encode(item.Key) %> Series</h5>
<ul>
<% foreach (var m in item)
{ %>
<li><a href="<%=Url.ManualLink(m.Id) %>" class="model-manuals">
<%= m.Id %></a> </li>
<% } %>
</ul>
</div>
IGrouping is a list of lists so to speak. With the "Key" being the grouped property in the source list.
The view must be the same type of the page. You would have to convert the output to the same type used by your view page.
You could also use a ViewData["Products"] to set the collection information you want to be visible on your View aspx page.

Creating content for rails-based applications

I'm facing a problem of cleaning up my application in Ruby on Rails. What I have is a pretty standard 3-panel, header and footer layout where different parts of the screen contain different functionality.
By that I mean for example that the header contains (among others) a select that allows one to select parts of the application and a context-dependent menu. The main content area contains obviously the most interactive stuff whereas side panels contain quick-links with stuff like shopping-cart preview, list of potentially attractive products for the customer, a selector to narrow down the list of options...
I was wondering how do I go about simplifying the design. Right now I have the stuff that provides data for the "common" stuff (as opposed to direct content that's placed in the center) called from all the actions (with a filter) but that doesn't feel right for me. I've read that "components" are also not the way to go for obvious performance reasons.
Is there something that's more like component-oriented (other frameworks do have that kind of stuff - Grails: <ui:include ../>, ASP.NET MVC: <% Html.RenderAction() %>)?
Best regards, Matthias.
You can use content_for for managing different location of a layout.
layout.html.erb
<body>
<div id="header">
</div>
<div id="content">
<div id="main">
<%= yield %>
</div>
<div id="side_bar">
<%= yield :side_bar %>
</div>
</div>
<div id="footer">
</div>
</body>
In your views use content_for to assign content to various sections of the layout:
<% content_for :side_bar do %>
<div>
<h1> Popular Posts </h1>
</div>
<% end %>
<div>
<h1> All Posts </h1>
<% #posts.each do |post| %>
<h2> <%= post.name %> </h2>
<% end %>
</div>
Reference:
Railscast
Edit 1
You can use partials to create reusable screen sections.
views/shared/_shopping_cart.html.erb
<div>
display the shopping cart details
<div>
views/posts/index.html.erb
<% content_for :side_bar do %>
<%= render :partial => 'shared/shopping_cart' %>
<% end %>
Essentially you can use:
Filters to set data
content_for to set layout
partials to display data
Combination of these three should allow you to componentize your solution.
layout
In app/views/layouts, a global layout application.html.erb defines the outer most layout (e.g. your 3-panel design).
The application layout can be overrided by a controller layout. For example, if you have a Tasks controller, you can add a tasks.html.erb in the app/views/layouts folder. The tasks.html.erb will be your default layout for any Tasks views (index, add, edit, etc.)
In application.html.erb or tasks.html.erb, you may add two partials, e.g. _header.html.erb, _side.html.erb. Those two partial will serve as the default panels for the views.
A better tutorial is at http://guides.rubyonrails.org/layouts_and_rendering.html

WebForms view engine looks ugly, any way to beautify it?

When I look at an MVC view which I wrote, it all looks like tag spaghetti to me especially with default color scheme of VS 2008. It's all <% yellow tags %> everywhere. It's really hard to distinguish server and client side code.
In classic ASP it wasn't that bad because usually server-side code blocks weren't as interleaved as lightweight MVC views. They were big chunks thus easily distinguishable. Now there is almost a 1:1 interleave between client-side and server-side code (e.g. one line client, one line server, goes on). I'm on the verge of having an epilepsy attack when I try to understand what a view produces.
If I could at least have a separate background color for server-side code, I think it would help. VS2008 doesn't allow it though.
I removed yellow background color from <%/%> tags, it looks better now, at least fixed the twitch in my eye, but it's still hard to track a view's flow.
I'm also ok with alternative view engines to fix this problem but I don't know if there is any that provides some of WebForms' luxuries:
Syntax highlighting for server-side code
Intellisense
Compiled
I looked at view engines listed in SO, specifically Spark, but I didn't like how it mingles with HTML code. I think it makes the mentioned problem worse.
Here is a sample code that I don't like:
<%# Page Title="" Language="C#"
MasterPageFile="~/Views/Shared/site.master"
Inherits="System.Web.Mvc.ViewPage<SomeModel>" %>
<%# Import Namespace="SomeHelpers" %>
<asp:Content ID="Content1" ContentPlaceHolderID="body" runat="server">
<h1><%= Html.Encode(Model.Title) %></h1><br /><br />
<% if (Model.Found) { %>
<% foreach (var item in Model.List) { %>
<div class="item">
<%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
<div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>,
<%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
</div>
<% } %>
<% } else { %>
Item Not Found <br />
<% if (Model.Items.Count > 0) { %>
Some Text Here<br />
<ul>
<% foreach(var i in Model.Items) { %>
<li><%= Html.SomeHelper(i) %></li>
<% } %>
</ul>
<% } %>
<% } %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="title" runat="server">
<%= Html.Encode(Model.Title) %> - Badass Web Site
</asp:Content>
After pasting the code I noticed that SO does a better job in highlighting server/client-side code properly :) I don't think this kind of highlighting is possible in VS2008 though (you'd have to change C# highlighting completely which I wouldn't prefer).
1. Partial Views are Your Friend
Use RenderPartial and separate partial views for any item that falls within a loop. This also can help with live updating with ajax because you can get the rendered HTML for a single item very easily without doing a page refresh.
<% foreach (var item in Model.List) { %>
<div class="item">
<%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
<div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>,
<%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
</div>
<% } %>
To:
<% foreach (var item in Model.List)
Html.RenderPartial("itemTemplate", item ); %>
2. Make the ViewModel pull its Weight
Do a lot more string manipulation and logic in your view models instead of loading up with single use Html helpers or similar. Maybe put HtmlEncoding in your ViewModels property setters? Or use methods like GetBoldedFirstName() in your ViewModel
Now this does kind of muddle the separation between C# and HTML markup BUT your Html markup will thank you for being cleaner. I personally don't like having 100s of single use Helpers floating around and think this technique makes the markup read better.
<div class="item">
<%= item.ProductID %> - <%= Html.SomeHelper(item.Description, 32000) %>
<div class="itemsub">(<%= Html.SomeOtherHelper(item.Customer) %>,
<%= Tools.AnotherHelper(item.OrderDate, item.ShipDate) %>)</div>
</div>
To:
<div class="item">
<%= item.ProductID %> - <%= item.FormattedDescription(3200) %>
<div class="itemsub">
(<%= item.GetCustomerName() %>,
<%= item.GetPrettyOrderStatusString() )
</div>
</div>
3. Use an Empty View
I try and keep my markup as stupid as possible and prefer to bake as much logic into the actual view model or action method I can.
For "empty" pages that I try to do is make a shared view for all my empty grids just like you would make a single 404 page for a website. Your controller should know that you have nothing to show and return the appropriate View.
You'll save is a lot of nesting and Count>0/empty/null checking in your markup.
Rob Connery has an excellent article on this topic as well:
http://blog.wekeroad.com/blog/asp-net-mvc-avoiding-tag-soup/
Well, Spark has everything you mentioned - syntax highlighting, Intellisense (works 80% though), views compilation and precompilation. And you're not forced to "mingle" HTML code, you can still use manual C# code, and even existing WebForms engine markup!
So either of the following will work in Spark
<% foreach (var product in Model) { %>
<li><%= product.Name %></li>
<% } %>
# foreach (var product in Model) {
<li>${product.Name}</li>
# }
<li each="var product in Model">${product.Name}</li>
To me the latter is a clear winner. But at least you have choice.

Get MultiView like behavior with ASP.NET MVC

I am trying to build a generic form submission system using ASP.NET MVC. I'd like to make it as easy as possible to create forms with a form view and a "success" view. Using the WebForms method, this was easy and could be accomplished with templates or multiviews. With MVC, I'm a bit stuck.
Here's what I'd like to emulate:
<% if (formNotSubmitted) { %>
<% Html.BeginForm(...); %>
<%= Html.TextBox("FirstName") %>
<%= Html.TextBox("LastName") %>
<input id='submit' type='submit' value='Submit' />
<%= Html.ValidationSummary %>
<% Html.EndForm(); %>
<% } else { %>
<p>Thank you!</p>
<p><img src='thanks.jpg' /></p>
<p>Other items here maybe.</p>
<% } %>
Ideally, I'd like to use Ajax, but also have it work with a straight POST. I'd also like to wrap this somehow to avoid the "if..else" code. What makes this harder than a typical ASP.NET MVC Ajax form is that the controller won't know what the success message/content is supposed to be. Most demos I've seen have the controller send back a message, but I need that code in the view.
Any guidance is appreciated.
You could it with the Controller
[AcceptVerbs("GET")]
public ActionResult Signup( )
{
// somecode that builds an html string
ViewData["form"] = htmlStringYouBuilt;
}
[AcceptVerbs("POST")]
public ActionResult Login( string username, string password )
{
// etc
}
then in the view
<%= ViewData["form"] %>
I've been able to use this technique successfully.

Resources