Map variables not usable in Scala Play HTML template - html

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>

Related

React renderDOM in multiple spots on same page?

I want react to render a DOM at multiple spots on my page. For example, if there's the class (.react-container), I want it to render the react component.
How can I do this if there are multiple spots on the page?
IMPORTANT: ArrayOne amount changes depending on where it's rendered.
APP.JS FILE
const App = () => {
return (
<div>
<div className="react-container">
{
ArrayOne.map(item=> (
<div className={
clsx({
'card': true,
'card-visible': state
})} >
<h2>{item.title}</h2>
<p>{item.text}</p>
</div>
))
}
</div>
</div>
);
};
INDEX.JS FILE
for (const el of document.querySelectorAll(".react-container")) {
ReactDOM.render(<App/>, el);
}
Code above is not running how I would like it to. It gives me two rows of only 1 card. When it should be 4 cards on row one and 1 card on row two. Console shows:
run query selector
running (4)
run query selector
running(1)
run query selector
running(1)
When I want it to do.
run query selector
running(4)
run query selector
running(1)
Interesting code. But i believe you are mixing some stuff.
get the layout working first, since seems you are not using React to do the layout.
<div>
<div className="react-container"></div>
<div className="react-container"></div>
<div className="react-container"></div>
<div className="react-container"></div>
</div>
render them into it with your lines.
for (const el of document.querySelectorAll(".react-container")) {
ReactDOM.render(<App/>, el);
}
Keep in mind, if you want to use your rest of code, you need to render it into a root, as in a typical React setup.
<div id="root"></div>
So pick one approach, either render once, but let react control everything underneath it, or render multiple times. You can't do both.

How can I identify which classes and id are been used in a html document?

Im trying to remove unused classes and id's in my site. Is there a function that I can use in the browser's JavaScript console (or other methods) to filter out the used/unused classes and elements.
Thanks in advance.
Something like this could work:
let classList = ["class1","class2","class3","class4","class5"];
classList.forEach(cl=>checkClasses(cl));
function checkClasses(classToCheck) {
if(document.querySelector('.'+classToCheck)){
console.log(classToCheck+' is being used');
}else {
console.log(classToCheck+' is not being used');
}
}
<div class="class1"></div>
<div class="class2"></div>
<div class="class4"></div>
<div class="class5"></div>

Show entity definitions inside title of divs

Having this HTML block in a React app:
<div className="my-class" title={myValue}>
<div>{myValue}</div>
</div>
The content of myValue is MyCompany™.
The inner div is showing the content correctly.
But the title, which appears when hover, replaces ™ with ™ and shows it like this: MyCompany™.
Why is this happening for the title? Is there a way to fix it?
You can use dangerouslySetInnerHTML, which is not ideal, but it is fine. So, the code should look like that:
let title = ["MyCompany", <span>™</span>]
<div className="my-class" dangerouslySetInnerHTML={{__html: title }}>
<div>{title }</div>
</div>
If your entity is dynamic, like you told me in the comments, then we can do the following trick:
const parseASCII = (string) => {
const htmlTextArea = document.createElement('textarea');
textarea.innerHTML = string;
return htmlTextArea.value;
}

AngularJS: ng-repeat, ng-if & JSON

I am having trouble with figuring out how to use ng-repeat and ng-if with JSON. I want a bar1 cluster to be displayed on the same line as a bar2 cluster if they have the same name.
The JSON looks like so:
{
"bar1" {
"name": "bar1"
"clusters": {
"1": {
"name": "Cluster 1"
}
...
}
}
"bar2" {
"name": "bar2"
"clusters": {
...
}
}
}
Right now I have a terrible solution where I nested the ng-repeat for bar2 inside the ng-repeat for bar1. The correct cluster is being displayed, but, unfortunately, this still generates the HTML for all of the clusters that are being looped through in bar2. At the very least, I don't want that extra HTML to be generated.
Angular
<div ng-repeat="(bar1clusterKey, bar1clusterValue) in blah.bar1.clusters">
<div>bar1 content</div>
<div ng-repeat="(bar2clusterKey, bar2clusterValue) in blah.bar2.clusters">
<div ng-if="bar1clusterValue.name == bar2clusterValue.name">
<div>bar2 content on the same line as bar1</div>
</div>
</div>
</div>
There are more bar1 clusters than bar2 clusters, and bar2 is a subset of bar1, so there aren't any unique bar2 cluster names.
I have a few questions: Is there any way to achieve my desired outcome without generating extra HTML with ng-repeat? Am I even using a sound nesting structure? Can I take care of this by using a service (I haven't even written an Angular service yet)?
EDIT: Here is a Plunk to show what I am talking about.
You could try angular filter to avoid ng-if
<div ng-repeat="(bar1clusterKey, bar1clusterValue) in blah.bar1.clusters">
<div>bar1 content</div>
<div ng-repeat="bar2 in blah.bar2.clusters| filter: {name: bar2clusterValue.name}">
<div>bar2 content on the same line as bar1</div>
</div>
</div>
If you put the ng-if on the same element as the ng-repeat, then the HTML generated for all the items that don't match the if will be commented out by the looks of it, which isn't totally ideal but it gets the job done. Perhaps a remove() could be piped in there somehow, but I didn't have any luck with that.
The final Plunk and the key HTML snippet:
<div class="flex-container" ng-repeat="(bar2clusterKey, bar2clusterValue) in
blah.bar2.clusters" ng-if="bar1clusterValue.name == bar2clusterValue.name">

Razor HTML Conditional Output

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