While the #Html.Actionlink() helper is very convenient for building <a> elements in the .cshtml files, is it possible to construct them inside C# strings, such that they are subsequently rendered correctly in the HTML output?
For example, if I assign a string variable a value similar to the following:
Book.ReadMore = "Click #Html.ActionLink(\"this link\", \"About\", \"Home\") to read more.";
And then I try to display it (the literal text plus the link) through my .cshtml page, using code similar to:
<p>#Model.ReadMore</p>
All I get in the browser is the whole string exactly as I typed it, including the #Html... etc:
Click #Html.ActionLink("this link", "About", "Home") to read more.
Now, for proper SoC, I know that it's not the best of practices to have HTML stuff included in C# code, but is it at all possible to get the proper <a> link in this scenario, instead of the string itself?
EDIT: More information - This string is just one item in a collection of about 20-30 strings (displayed using a for loop in the View). Only a small handful of those items need a link (which is different in each case). Since, as mentioned above, I agree that it's obviously not good practice to use Razor/HTML in Model code, I'm trying to get a simple approach (if possible) which would give me the flexibility of building the link somewhere at the right place, while still yielding the maintainability of MVC SoC.
There must be a "right" way of doing this, which is simple yet maintainable.
Your model should not contain HTML, that's a view concern and belongs in view code. Probably you should be using a Razor helper.
In your App_Code folder (create one if you don't have one), add a file, ReadMoreHelpers.cshtml:
#helper ReadMore() {
<text>Click #Html.ActionLink("this link", "About", "Home") to read more.</text>
}
Then in any view:
#ReadMoreHelpers.ReadMore()
And that will output what you want. If you insist on putting that property in your view, you could do:
Book.ReadMore = "Click " + #Html.ActionLink("this link", "About", "Home").ToHtmlString() + " to read more.";
Then in your view, make sure you use Raw:
#Html.Raw(Book.ReadMore)
However, I couldn't recommend more strongly that you do not put HTML in your model properties.
I don't think so. The Razor view engine will interpret the ActionLink code during run-time while stuffing it as part of a C# string will be interpreted during compile time.
Related
I've looked at a dozen or so ways to do this and I'm having a very hard time getting things to work. The issues I've run into fall into one of two categories:
1) A full MVC setup is assumed in the examples I'm looking at. In my case, I'm sending an email from a WebApi project and will not be using any of the pre-wired MVC view functionality provided by the ASP.NET project stub.
2) Libraries are asking for some complex setup I don't really think I need, specifically RazorEngine asking me to set up a TemplateManager, when all I need to do is give a .cshtml file a model and get the parsed results back.
Pardon the noob question; I'm working on my first .NET project here. Thanks!
I had to do the same thing for different reasons a while ago. I had found this class that I used to render the view into a string: https://github.com/RickStrahl/WestwindToolkit/blob/master/Westwind.Web.Mvc/Utils/ViewRenderer.cs
Use it like this:
var r = new ViewRenderer();
var renderedView = r.RenderViewToString("~/Views/MyView.cshtml");
If you need to pass a model to the view, call it like this:
var renderedView = r.RenderViewToString("~/Views/MyView.cshtml", model);
Where model is what your view expects.
Suppose I have a model with some string property.
Imagine also that this string property is actually a comma delimited list of values.
If I want to make a form to update values on my model it would be easy enough to call:
#Html.TextBoxFor(model => model.myCommaDelimitedProp, new { #class = "form-control", placeholder = "CommaDelimitedPropValue" })
However, that is not good enough for the intended application.
I would like to have a custom EditorFor() that could somehow take my property, use string parsing and next generate an array of text boxes to display the pre-existing values.
That would also be relatively trivial.
However, what I cannot seem to solve, mainly because I lack client side experience (js, jquery, angular, ...):
How could I make my editor such that there would be a small button so that I could dynamically add rows, fill them such that, upon form submission, I could string the new values onto the pre-existing string.
So specifically, what would any of you use to achieve this client side behaviour?
I just need some help to be put on the way...
You can achieve this with editor templates. There's a quick intro I threw together on my blog. The only additional thing you'll need is UIHint. Since you won't be able to rely on a specific C# type or DataType annotation to determine that this should be treated as a comma-delimited property. You can just explicitly tell Razor what template it should use. For example:
[UIHint("CommaDelimited")]
public string MyCommaDelimitedProperty { get; set; }
Which would correspond to the editor template: Views\Shared\EditorTemplates\CommaDelimited.cshtml. Once you set up that view how you like it. Then in your form you just call:
#Html.EditorFor(m => m.MyCommaDelimitedProperty)
EDIT
I'll leave my previous answer because it could still be helpful in terms of being able to generate a control for a specific type of thing. You actually may still need to use it to get the right set up on your field to make the JS work properly.
However, when it comes to the client-side handling of this, I figured there had to be something out there already to solve this problem. (Never do more work than you have to.) A cursory search turned up a little script called Tokenfield for Bootstrap. I'm not sure if you're using Bootstrap or not. If not, I also found jQuery Tokeninput and jquery.token-field. I'm sure there's others, as well.
I'm struggling with the following problem and I can't find an acceptable way to solve it.
My challenge: write out HTML comments just before the actual property value in a Razor view.
This is my (simplyfied) Viewmodel:
public class Article
{
public string Title {get;set;}
}
To write out this title I simply do this in my Razor view:
<h2>#Model.Title</h2>
Now I want to write out a html comment just before the actual title so the generated HTML looks like this (simplyfied):
<h2><!-- some parameters for a 3th party system --> This is my title</h2>
The HTML comment comes from an Attribute I applied to the 'Title' attribute. It's value is generated, so the attribute-value is added at runtime using the TypeDescriptor from the .NET framework.
Now I know I could achieve this by simply writing out all my properties using an HTML helper. Like this: #MyHelper.Write(m => m.Title)
But since potentially ALL my properties need this HTML comment I want to avoid the use of an HTML helper since it clutters the View and doesn't make the view look nice and (more) readable.
This is what I have tried:
Created a custom Razor base page (Inheriting from WebViewPage<TModel>). And overwriting it's 'Write' method.
This kind of works but the BIGGEST problem here is that I don't know which property is been written out at that moment. There is no way of getting the current property name in the 'Write' method. So now I dynamically search my Model to find a property with the value that's been written out and prepend the HTML comment from the attribute.
My question: is there another approach to accomplish what I want. As sais before: I want to avoid using an HTML helper to write out all my properties. (Think about loops, etc. It's just not nice).
Also, adding this HTML comment in my Controller is no option since:
it's not part of the actual value. Is a sort of metadata.
The HTML comment should be added to int's, double's and DateTime's. There is no way to adjust a double property to include a string. (Image a List<DateTime>. All date's need this HTML comment)
the HTML comment should be added based on a web.config setting. Yes or No. (The actual HTML comment is different for each value of a property)
I realize this question is rather long. Sorry for that. Any thoughts are appreciated.
You can use the existing #Html.Raw(Model.Title)
Alternatively you can use a display templates. Add a UIHintAttribute to the properties you wish to behave this way.
public class MyModel
{
[UIHint("Raw")]
public string MyString { get; set; }
}
Create a new display template called Raw.cshtml that accepts model of type string:
#model string
#Html.Raw(model)
Then in your view you can use:
#Html.DisplayFor(m => m.MyString)
This still requires that you use a helper (DisplayFor). This is a recommended practice that allows you to easily change the behavior of one or many fields with minimal code changes.
I am relatively new to ASP.NET MVC, and am very impressed with the clarity of the platform so far. However, there is one aspect that I find uncomfortable.
At first, I accepted the fact that when I say
return View();
I am calling a helper method that returns an ActionResult, and makes some assumptions about which view to present, route values, etc. But lately I have been writing code that looks more like this:
return View("Index", new { id = myID })
because it is immediately clear to me what's happening by reading that single line of code.
Lately I have been struggling with the fact that I can have an Index.ASPX view open on the tabs, and I can't immediately tell where it comes from because the IDE doesn't highlight the current tab in the Object Explorer. I haven't resorted to changing the names of the files to ControllerNameIndex.ASPX, but I do put a title in the view that is more specific. Still, it doesn't help much.
How do you deal with these kinds of ambiguities?
I think you answered your own question.
There's no hard rule preventing you from calling your views very specific names, such as "ListOfFooBars" or "EditFizzBuzz" or "AddNewGeeblup". The naming convention for the default view engine only specifies that there's a folder corresponding to your model name under views, and there's an ASPX or ASPC file under that folder that corresponds to your view name.
I'm currently trying to user actionlink helpers in a way that I don't think was described in NerdDinner.
Lets say I am on this page
/Dinners/
and on that page there is a list of dinners, ok fine and working
now lets say I want to goto a new section of the site, which I have created a new controller MenuItemsController
so lets say I want to goto a new part of the website that manages menu items.
So going to
/menuitems/3
would bring up all the menu items assoicated with dinner id 3.
This is also working.
I am having trouble, linking to each of the menu item pages, because when I use the actionlink code, without much modification i get this
dinner1 = link /dinners/menuitems/3
rather than
dinner = link /menuitems/3
The actionlink code i am trying is
<%= Html.ActionLink("Menu Items", "/menuitems", new { id=item.id })%>
you can see the / there. This feels wrong.
I wasn't sure if this post was talking about the same problem or not.
how do i have links to root controllers in site.master in asp.net mvc
Are action links the completely wrong thing for me to be using here, becuase they are binded directly to the controller I am currently inside of?
If so, what would be the best method for me to achieve what I am trying to do, and also add further complexity like linking to create/edit/delete methods?
Just get rid of the slash and specify the controller and action explicitly:
<%= Html.ActionLink("Menu Items", "Item", "menuitems",
new RouteValueDictionary { { "id", item.id } })%>
You don't give an action name in your examples, so I guessed "Item." Insert the correct action name, obviously.
The current controller name is used if you use one of the ActionLink overloads which don't take a controller name.
I've written an in-depth explanation of routing, ActionLink, and more.
I oddly after much searching all day just found this page,
http://devlicio.us/blogs/derik_whittaker/archive/2008/03/06/link-building-101-with-asp-net-mvc.aspx
it seems like method 3 may be what I need, I'll try it when I get home.