Razor HTML Conditional Output - razor

I have a list of items I want to output as the contents of a main (the main in not included below). Each Item has 3 attributes: a Section Name, a Label and a Value. Each item is enclosed in a and everytime the Section Name changes I have to open a (and close the previous one, if any). I'm using a Razor view with this code:
#foreach (LocalStorageItem lsi in Model) {
string fld_name = "f_" + lsi.ItemName;
if (lsi.SectionName != sn) {
if (sn != "") {
Html.Raw("</fieldset>");
}
sn = lsi.SectionName;
<h2>#sn</h2>
Html.Raw("<fieldset>");
}
<div class="row">
<div class="ls_label">#lsi.ItemName</div>
<div class="ls_content" name="#fld_name" id="#fld_name">.</div>
</div>
}
#if (Model.Count != 0) {
Html.Raw("</fieldset>");
}
The problem is: each time the Section Name changes no fieldset tag (open and/or close) is generated. Where am I wrong? If I don't use Html.Raw (or #: as an alternative) the VS2010 parser signals an error.

Calling Html.Raw returns an IHtmlString; it doesn't write anything to the page.
Instead, you should write
#:</fieldset>
Using #: forces Razor to treat it as plain text, so it doesn't need to be well-formed.
However, your code can be made much cleaner by calling GroupBy and making a nested foreach loop.

I really think that the use of #: to work around such code is an abuse of that escape sequence. The problem should be addressed instead by correctly refactoring the code so that balanced tags can be easily written:
#foreach(var section in Model.GroupBy(i => i.SectionName)) {
<h2>#section.Key</h2>
<fieldset>
#foreach(LocalStorageItem lsi in section) {
string fld_name = "f_" + lsi.ItemName;
<div class="row">
<div class="ls_label">#lsi.ItemName</div>
<div class="ls_content" name="#fld_name" id="#fld_name">.</div>
</div>
}
</fieldset>
}
12 lines of code instead of 18

Related

Angular variable output if/else HTML output not working

I'm confused. I'm trying to check in a loop if the outputted key is a mail key, if yes, I want to wrap the output into a <a> tag to make the email clickable. Somehow it's not working and it's printing the whole content inside the div:
<div>{{contactInfo.key === 'mail_outline' ? '' + contactInfo.value + '' : contactInfo.value}}</div>
Current result on the page:
{{contactInfo.key === 'mail_outline' ? '' + contactInfo.value + '' : contactInfo.value}}
I'm not sure if I understood the whole thing correctly. I'm coming from PHP and there I can do something like this. Can someone please explain me my issue?
If you surround the html elements inside the div with quotes like you did, you are telling angular that the divcontent is actually a text value. So it will end up displaying your condition as text.
You can add dynamic html by binding to innerHtml property like suggested, however I find it cleaner to keep display logic in the template using one of the options below. Besides, they will avoid the extra overhead of calling a function every round of change detection
*ngIf
<div >
<a *ngIf="contactInfo.key === 'mail_outline'" href="{{contactInfo.value}}">{{contactInfo.value}}</a>
<ng-container *ngIf="contactInfo.key !== 'mail_outline'">{{contactInfo.value}}</ng-container>
</div>
Or use *ngSwitch, which will be more adequate if you've got several different cases
<div [ngSwitch]="contactInfo.key">
<a *ngSwitchCase="'mail_outline'" href="{{contactInfo.value}}">{{contactInfo.value}}</a>
<ng-container *ngSwitchDefault>{{contactInfo.value}}</ng-container>
</div>
Use angular Html binding. Update your html
<div [innerHtml]="getLink()"></div>
In your typescript
getLink(){
if(this.contactInfo.key === 'mail_outline')
{
return `<a href='${this.contactInfo.value}'>${this.contactInfo.value}</a>`;
}
else
{
return this.contactInfo.value;
}
}
Thanks.

Kendo PanelBar HTML string as Content

I am implementing a view where there are tabs (Kendo TabStrip) and inside these tabs are some accordion items (Kendo PanelBar).
I dinamically draw the tabs using a foreach, and in each tab, I also use a foreach to draw the accordion. The thing is that, the content of each accordion item is a HTML string (like: <p>Some <strong>text</strong></p>).
In chrome all work fine, but with IE8 everything goes out (because the page HTML mixes with the string HTML).
This is my code:
#(Html.Kendo().TabStrip()
.Name("tabAyuda")
.HtmlAttributes(new { style = "" })
.Animation(false)
.SelectedIndex(0)
.Items(tabAyuda =>
{
foreach (KeyValuePair<string, IList<ElementoAyuda>> accion in Model)
{
if (!string.IsNullOrWhiteSpace(accion.Key))
{
tabAyuda.Add().Text(accion.Key)
.Content(#<text>
#(Html.Kendo().PanelBar()
.Name("panelbar" + accion.Key)
.ExpandMode(PanelBarExpandMode.Single)
.Items(panelbar =>
{
foreach (ElementoAyuda elemento in accion.Value)
{
panelbar.Add()
.Text(elemento.Head)
.Content(elemento.Detail);
}
})
)
</text>);
}
}
})
)
I've also tried with this code inside .Content:
.Content(#<text>
#Html.Raw(elemento.Detail)
</text>)
But I get this error: Custom tool error: Inline markup blocks (#<p>Content</p>) cannot be nested. Only one level of inline markup is allowed.
Any advice??
Thanks in advance!
Solved, it was my fault. Some of the HTML strings had invalid syntax, but somehow in Chrome works XD

AngularJs - how to support a textbox with hyperlinks

I'm new to Angular but I'm trying to implement a textbox that allows users to enter in links. I only want to support links, and otherwise I want to block all html from being presented as such. I could theoretically use something other than a textarea, but my requirements are that it must be bound to a variable in my scope (right now with ng-model) and I cannot accept html tags other than '< a >'
Here is my example plnkr
In the example, I would like the second seeded item to display as a link, blue and underlined. However, the third item should display as it is currently shown (without interpreting it as html).
HTML:
<textarea maxlength="160" ng-model="val.text"></textarea>
<div class="btn" ng-click="submit()">Submit</div>
<br><br>
<div ng-repeat="item in items">
{{display(item)}}
</div>
JS:
$scope.submit = function() {
if (!$scope.val.text) return
$scope.items.push($scope.val.text);
}
$scope.display = function(txt) {
return txt;
// something here? if txt contains <a> and </a> indicate
// that we should display as html
}

Map variables not usable in Scala Play HTML template

I have the following code in one of my Play .scala.html templates:
#formats.map(format => {
<div id="#format">
{format}
</div>
})
formats is a Seq of enumerations. The divs are created, with the proper "format" content (each contains a different format string), however, the ids are never set correctly. The id of each div is literally set to "#format", like this:
<div id="#format">
OneOfTheFormats
</div>
<div id="#format">
AnotherFormat
</div>
I've tried making the code <div id="{format}">, <div id={format}>, and <div id=#format> with no luck. It's odd, because I have done similar things in my other templates, but perhaps it's not working because of the special map case... maybe because format is a created argument, and not passed into the template?
UPDATE:
I tried the following, as someone below suggested:
#{
def createDiv(f: String) = {
<div id="#f">
{f}
</div>
}
formats.map(f => {
createDiv(f.toString)
})
}
Again, The formats are printed correctly inside the div, but the ID is never set. I'm beginning to think this isn't possible. I've also tried <div id="#f">, <div id="{f}">, and <div id="#{f}"> with no luck. Oddly enough, in order to print the format inside the div, I have to use {f}, and not #f. Still struggling here...
UPDATE 2:
It works if I do the following: <div id={f}> ... no quotes! God damn.
As I know, there are some limitations for declaring new variables in new templates, but you can use such a workaround:
#createDiv(format: String) = {
<div id="#format">
#format
</div>
}
And use it in your code like this:
#formats.map(format => {
createDiv(format.toString)
})
This worked for me. Hope this solution suits you.
it seems that there is name collision then you use "format" as a variable name probably because of String.format, try with different name
#formats.map{f =>
<div id="#f">
#f
</div>
}
The following worked for me:
#{
def createDiv(format: String) = {
<div id={format}>
{format}
</div>
}
formats.map(format => {
createDiv(format.toString)
})
}
Note the enclosed #{ } block, and that there are no quotes around the id part of <div id={format}>.
UPDATE:
I ended up doing something a bit cleaner - I used a separate template file. The code looks a bit like this now:
#formats.map(f => {
// do some other stuff
// render format subview
formatSubView(f, otherStuff)
})
And the sub-view template looks like the following:
#(f: theFormatEnum,
otherStuff: lotsOfOtherStuff)
<div id="#f">
<img src="#{routes.Assets.at("images/" + f + ".png")}"/>
// etc, etc
</div>

Add non breaking space within razor helper method

How can I include a non-breaking space ( ) within a Razor helper method? Here's the helper in question:
#helper RenderClipResult(Clip clip, IList<string> searchTerms)
{
<div class="result">
<!-- other clip stuff -->
#if (clip.ThirdPartyMaterials != null && clip.ThirdPartyMaterials.Count > 0)
{
<p>
<span class="heading">Third Party Material</span><br/>
#foreach (var material in clip.ThirdPartyMaterials)
{
#AddElement("Description", material.Description, searchTerms) #AddElement("Cost", material.Cost, searchTerms)
<br />
}
</p>
}
</div>
}
AddElement is another custom helper. The output I'm looking for is something like this:
Third Party Material
first entry
second entry
third entry
I could wrap the AddElement line in a span tag for styling but it's another html tag and css rule, just to indent some text by a single character width. Might have to go that way as Razor is not able to parse the space
Add #: before your non-breaking space html code
The second option is to use construction: #Html.Raw(" ") . This also can be user with multiplied nbsp-signs, so you can put result of the string builder to RAW function.