Return HtmlString From Model & Display Within View - asp.net-mvc

Let me preface this by saying that I know that I can use HTML.Raw() to display HTML contained in a string. However, I believe that the purpose of MVC is to separate the code logic and to allow front end developers to focus on UI and not logic. Therefore, I try to pass everything I can from the model exactly as I want it to look.
That brings me to my question. I have a model that contains an address. I have written a function that returns the address in a few different versions (single line, two line, multiline) and I'm setting these as HtmlString objects.
public HtmlString TwoLine { get { return ReturnFullAddress(2); } }
/* function removed for brevity */
However, when I write the following razor code:
#Html.DisplayFor(modelItem => item.Address.TwoLine)
No text is returned within the view. When I debug this, there IS a value properly assigned within Address.TwoLine, but it is within { } (which I thought was strange).
How do I make this work, or why doesn't this work?

DisplayFor() doesn't know how to handle HtmlString properties.
You should just print the value directly: #Model.Address.TwoLine

Related

Is returning different view with object bad practice in ASP.net MVC 5?

I need to pass objects between ActionMethods and Views in an ASP.net MVC 5 app.
I'm using it for a multi page signup - and for a multi page payment.
Is this bad practice? I haven't seen a good way to pass objects between different controllers.
Code:
public ActionResult Join1()
{
//
return View("Join2", MyObject);
}
[HttpPost]
public ActionResult Join2(MyObject MyObj)
{
//manipulate object
//return
}
It seems to be an effective way to do it - though I haven't seen many people do it this way. I haven't seen objects being passed between action methods much at all.
Is there a flaw in this approach, a better way of passing models between Views - or should each ActionMethod stick to passing simple data with, say, TempData instead of objects?
Why haven't I seen any sample projects doing things like this?
I've seen return RedirectToAction("act"); plenty - but that is Get and passing an object in a URI is limiting - and I don't want users to be able to manipulate or see the data being passed.
thx.
Unless I have misunderstood the description, your code is not doing what you think it's doing. That first return View("Join2", MyObject); statement is not executing the second ActionMethod, it is only passing your data into the View that happens to have the same name as the second method. Therefore the code implied by //manipulate object will not run before that View is rendered and sent back to the user.
Assuming the View file Join2.cshtml exists, and it contains the default #using (Html.BeginForm()), then users submitting the form will cause the Join2 Action to be executed and the same view rendered with the manipulated data - unless, of course, you add another return View() statement that names a different View.
The reason you haven't seen this done much is that the MVC convention is to have a View named the same as the ActionMethod, this makes the code slightly simpler and also much easier for other ASP.NET developers to understand because it is what they are expecting to see.
If you want the form rendered by each View to then execute a different ActionMethod when it is posted back, the place to do that is in the View code, where Html.BeginForm() has several overloads that allow you to do just that, e.g. in Join.cshtml you could write:
#using (Html.BeginForm("Join2", "JoinController"))
{
// form fields and stuff
}
// Produces the following form element
// <form action="/JoinController/Join2" action="post">
To address the final part of your question, "I don't want users to be able to manipulate or see the data being passed", sorry to say it but your proposed code doesn't prevent that: users can see the data in the web form, before it is ever posted back to the Join2 method; they can manipulate the data by sending an HTTP POST containing any data they want back to the Join2 method.
If you absolutely, positively need to actually execute Join2() from within Join(), before anything is passed back to the user, then you can call it just like any other C# method:
var myResult = Join2(MyObject);
Then you have an ActionResult object that you can manipulate or return straight to the browser. But why you would want to do this, is beyond me.

ServiceStack Razor - Html.RenderAction equivalent

I have the requirement to use Html.RenderAction like you would in ASP.NET MVC.
For instance I have a Home Page with News and Products on.
I would like to do for instance
#Html.RenderAction("/api/products/featured")
Which would start a new service call and output the template to the html stream.
Is this possible using ServiceStack Razor and if so how do I accomplish it?
The PartialExamples.cshtml test page shows different examples of rendering a razor view inside a page, e.g:
Using the new RenderToAction() method which lets you execute a Service and it's rendered partial view with a route and QueryString, e.g:
#Html.RenderAction("/products/1")
This also takes an optional view name if you want a different view than the default:
#Html.RenderAction("/products/1", "CustomProductView")
There's also the normal Html.Partial() to specify which view and model you want to render in the page, e.g:
#Html.Partial("GetProduct",
base.ExecuteService<ProductService>(s => s.Any(new GetProduct { Id = 1 })))
ExecuteService is simply a wrapper around the equivalent ResolveService in a using statement, i.e:
#{
Response response = null;
using (var service = base.ResolveService<ProductService>())
{
response = service.Any(new GetProduct { Id = 1 });
}
}
#Html.Partial("GetProduct", response)
The new RenderToAction() method in Razor Views was added in v4.0.34+ which is now available on MyGet.
*I may be duplicating my answer or I lost it somehow
Looking at the ServiceStack.Razor.ViewPage class there is an Html property of type ServiceStack.Html.HtmlHelper. I don't see 'RenderAction' as a method (or extension method) on this class so it doesn't appear to be available. There is a 'Partial' method that takes the ViewName and an overload that takes a ViewName and an object. Based on your above comment this doesn't appear to be a useful solution.
If I'm correct about the above, I think you'd need your 'Featured View Template' to pull in the data. Could add soemthing like
{ FeaturedResponse products = new JsonServiceClient("http://localhost").Get<FeaturedResponse>("/api/products/featured"); }
to your template. This would allow you to use the products variable like a Model.
Or, use JavaScript to pull the data into the template. You would have have to use JavaScript to get your data into the HTML elements, though.
You could then render the template using #Html.Partial('Featured')
Hope this helps.

Replacing content in Razor selectively

Is it possible to do something like this in Razor:
#using(Html.CMSContent("TermsOfService"))
{
<text>
<!-- Some default content goes here if the "TermsOfService"
item doesn't exist in my content table in the database -->
</text>
}
When the view is being processed, if the CMSContent helper doesn't find a content item named "TermsOfService" in some database table I've set up, it'll output the default content.
Otherwise, it'll replace the default content with what's in the database?
I'm trying to build a CMS/application hybrid, so that the Marketing department can override the text in my app if they want.
CLARIFY
The reason I'm doing this is to make the code super-easy to read and write. I'm going to end up with these little content blocks all over the place, so I'm hoping to minimize how much a developer has to type to get this functionality.
I'm also expecting that 99% of the time, the default text is what's going to show up. But I'd like to have the 1% cases handled where we need to quickly change some text without having to re-deploy the app. So, having the default text as close to its context as possible is important (ie, I don't want the developer to have to jump to a different file to see what the default text is - nor have to jump to the top of the page).
For example, when you do #using(Html.BeginForm()) it'll automatically emit the start and end <form> tags. I'm hoping for something clever like that.
What does Html.CMSContent("..") return? Could you store the return value of this helper into a variable and then check to see if the call returned a database value, if so display content otherwise display default text? Here is some quick code for what I describe not sure if it works 100% in your case since you did not specify what your helper returns
#{
var terms = Html.CMSContent("TermsOfService");
}
#if (terms != null && terms != "") {
#terms
} else {
#: Default Terms of Service
}
I think I figured out what I was looking for. It uses an if instead of using:
if(!Html.CMSContent("TermsOfService"))
{
<text>
Default content goes here
</text>
}
For the helper:
public static bool CMSContent(this HtmlHelper Html, string BlockName)
{
if (blockname in database)
{
var blockdata = (retrieve from database);
Html.ViewContext.HttpContext.Response.Write(blockdata);
return true;
}
else
{
return false;
}
}
Although I'm not sure that using Response.Write is the best way to do it.

conditional logic in mvc view vs htmlhelper vs action

I have a large view that needs some conditional logic to decide which of several html chunks to render in the middle of the view. I have a property on my model which can have several different values which determines the html to be output.
I would normally put conditional logic in an html helper, but given that each output is a fair chunk of html, I am not sure that escaping these in a c# file would be great. I could also put the logic in the action and render different views but given that the majority of the view is the same, this does not seem great either. So I am left with multiple if statements in my view (or partial?) which also seems ugly (and is obviously untestable).
What is the best way of doing this?
(I am using MVC3 in case there is something new and funky I can use!)
I usually put separate visual chunks in their own partials. Then my view conditionally calls each partial with Html.Partial. This keeps you main view from bloating.
In general, I try to avoid Html.Helpers that contain more than a single element.
Something like:
#if(Model.HasA)
{
#Html.Partial("widgetdetails-hasa")
}
#if(Model.HasB)
{
#Html.Partial("widgetdetails-hasb")
}
// etc
IMHO logic like this is fine for a view:
#if (Model.ShouldShowSomeSection)
{
... some large chunk of HTML
}
else
{
... some alternative
}
I agree with the answer from #bmancini , but here's what I'd do slightly differently:
I would logically group those 'several html chunks to render' into different partial views :
_partialViewA.cshtml and _partialViewB.cshtml
I then would use extension methods and have my logic in a Helpers folder, then Html sub-folder like this:
using System.Web.Mvc.Html;
public static class SomeViewHelper
{
public static MvcHtmlString OutputHtmlString(this HtmlHelper helper , SomeModel model)
{
if(model.HasA)
{
return helper.Partial("_partialViewA", model)
}
if(model.HasB)
{
return helper.Partial("_partialViewB", model)
}
}
}
This would remove all the logic from the view which would now only have this code:
#Html.OutputHtmlString(Model);
At least this would remove the 'ugliness' and avoid the conditional statements, and also avoid 'escaping the html chinks in C# code'...
Of course I would have to reference the Helpers.Html folder with a #using statement in the view.

dynamically generating an Enumerable object of a specific type from a string representation of the type

I'm trying to design a solution in MVC in which a string representation of a class is passed to the controller which should then build a grid with all the data belonging to that class in the DB. (I'm using an ORM to map classes to tables).
//A method in the Model that populates the Item Property
foreach (MethodInfo method in sDRMethods)
{
if (method.Name.Contains(_domainTable))
{
Items = method.Invoke(repositoryObject, null);
break;
}
}
//View uses this Items property of the Model to populate the grid.
public object Items;
//_domainTable is the name of the table/class (in string format).
//repositoryObject is the object that has methods to return IEnumerable<class> collection object of each type.
The problem I have is that I do not know how to cast the "Items" property in my view to iterate through it and build a grid.
I have tried using the "http://mvcsharp.wordpress.com/2010/02/11/building-a-data-grid-in-asp-net-mvc/" but the generic extension method is expecting to know the specific type that it should work with.
I would prefer to use MVC but it looks like I cannot easily have this working(which is very hard to believe).
I really don't like the sound of what you are trying to do. Why convert the table to a string?
The only time you would convert to a string, is when the view gets rendered. And that, in most cases, should be left to the MVC framework.
The code you mentioned uses an HtmlTextWriter which is fine, because it will render straight to the response.
However, it sounds as if you are trying to reinvent the wheel by rendering everything to a string, rather than leaving that to the framework.
Note that in MVC the views are just templates for rendering strings, which is, if I have understood you, exactly what you need.
So, if I have remotely understood what you are trying to do, and it is a big if because your post is not clear, you should pass your class to view as part of the strongly typed model, and then write some basic design logic into the view.
If I am right, which is not certain, I think you have misunderstood how MVC works.
Have a look at a few examples of how to use views to render the data in a model. The model can be any class, it can be an IEnumerable, a list, whatever, and you can use foreach loops in the view to render out what you want, how you want it.
In this sense, MVC is very different to writing custom controls in plain vanilla ASP.NET.
Thanks for your reply awrigley.
The requirement is quite simple. I perhaps made it sound awfully complex in my post.
On an Index view, I have to populate a dropdownlist with all the tables of the application that are system lookup. The "Admin" of the app, selects an item from the dropdownlist which should show the contects of that table in a grid so that the admin can perform CRUD operations using that grid.
What I am trying to do is, pass the selected item (which is the name of the table) to the controller which in turn passes it to the ViewModel class. This class uses reflection to invoke (code shown in my original question) the right method of a repository which has got methods like:
public IEnumerable GetAllTable1Data()
{
.....
}
The problem I have is that when I invoke the method, it returns a type "object" which I cannot cast to anything specific because I don't know the specific type that it should be cast to. When this object is passed to the view, the grid is expecting an IEnumerable or IEnumerable but I do not know this information. I am not able to do this:
(IEnumerable)method.Invoke(repositoryObject, null)
I get: cannot cast IEnumerable to IEnumerable
I (kind of) have the grid now displaying but I am using a Switch statement in the view that goes:
Switch(SLU_Type)
{
case "SLU_Table1": Html.Grid((IEnumerable)Model.Items);
case "SLU_Table2": Html.Grid((IEnumerable)Model.Items);
.....
}
I don't like this at all, it feels wrong but I just cannot find a decent way!
I could have partial views for each of the system look up tables but for that I'll have to add around 30 partial views with almost exactly same code for the Action & View. This does not seem right either!
Hopefully, this gives you a better understanding of what I'm trying to achieve.

Resources