Aurelia not outputting attribute with string interpolation in repeat - aurelia

Is there any reason why a repeat.for binding would remove attributes from elements inside the repeater?
<div repeat.for="i of model.someArray.length">
<label>Some Array - Index ${i + 1}</label>
<input value.bind="model.someArray[i]" some-custom-attribute="someArray[${i}]"/>
</div>
and that some-custom-attribute is not being output within the repeat, but if I were to remove the string interpolation within there then it outputs fine.
== Edit ==
I have put it in a comment but just to make sure everyone is on the same page, ideally this is the output I expect:
<input value.bind="model.someArray[i]" some-custom-attribute="someArray[0]"/>
The some-custom-attribute is not an aurelia attribute, its pure HTML that a 3rd party JS library uses, so the goal here is to get the textual value of the index into the textual attribute value.

model.someArray.length is a number, not an array. You need to iterate over the array. If you do need the current index, the repeater provides the $index property for you to use.
Your code should look like this:
<div repeat.for="item of model.someArray">
<label>Some Array - Index ${$index + 1}</label>
<input value.bind="item" some-custom-attribute.bind="item"/>
</div>
To answer your original question, doing some-custom-attribute="model.someArray[${i}]" makes Aurelia think you are trying to pass a string value to the custom attribute. You can see that in the following gist: https://gist.run/?id=eed8ac8623ff4749aa5bb93c82a7b1fb I've created a custom element that just pushes whatever value it is given in to an element on the page. Note!!! Don't ever do what I'm doing here! I just did this this way so you wouldn't have to open the js console. To actually get a value passed in, you would need to use some-custom-attribute.bind="item" or (to do things how you are doing things, some-custom-attribute.bind="someArray[i]"

Related

Looking for child elements in a TagHelper

I am writing a custom TagHelper that should change its behavior based on what child elements its owner tag has. This will be used for substituting custom html elements in a localized string.
Here's a simple example of what I'm trying to do:
<h2 locale-key="Hello, %1!">
<span class="bold" locale-parameter="1">#userName</span>
</h2>
In the example above, the TagHelper for LocaleKey will change the content of the <h2> tag to a localized string. If this is a simple string with no parameters, then I can just simply use output.Content.SetHtmlContent() in the TagHelper to set the h2's content to the string. However in this case, the localized string has a parameter (%1), and I want the corresponding child element (<span parameter="1">) to be substituted in the final output. So the final rendered html would look like this:
<h2>Hello, <span class="bold">Lázár Zsolt</span>!<h2/>
To achieve this, I should be able to see all the child elements in the DOM from the LocaleKey TagHelper, so I can generate the correct HTML code. In the TagHelper's ProcessAsync method I can access the TagHelperContext and the TagHelperOutput, but I couldn't find any information about child elements in either of these objects while debugging. Is this possible somehow?
Another approach is to leave the string as is, with the %1 parameter key, and then use the child ParameterTagHelper to substitute itself into the parameterized string. To do this, I'd have to see the siblings of the current tag from the TagHelper.
Recursion would also be fun (e.g. the <span> is also localized and has its own parameters), but for the scope of this question, I'd be happy if I could get this to work without recursion.
I posted too soon again... There is a GetChildContentAsync() method in TagHelperOutput that will... get the child content asynchronously.

How to use `v-if` and `v-for` on the same element?

Hi I'm trying to figure out how to use v-if on a iterated element which also uses v-for. I need to check if the current element has any of a series of classes, which are numbers.
so the classes of each article would be:
<article class="post-item post-guide 12 22 19 30 55">...
this is the HTML that renders all:
<article v-if="showIfHasClass" :class="'post-item post-guide ' + guide.categories.toString().replace(/,/g, ' ')"
v-for="(guide, index) in guides" :key="index">
<header>
<h1 class="post-title">
{{ guide.title.rendered}}
</h1>
</header>
</article>
I have tried with methods that check the class of all elements, that works, but i'm trying to use a clean Vue built-in solution with v-if without success, i'm not able to retrieve the class of this in a successful way.
Should showIfHasClass be a computed property? I have tried with that too... but it seems, I'm missing something along the way.
my data I have to check against is an array:
data:{
guides: [...]
selectedCategories: [1, 22, 33, 100, 30];
}
or maybe it is better to directly loop over the guides and check if they have the selectedCategory or not, then remove the element from the guides data array?
What is more effective?
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the <template> tag.
The <template> tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides">
<article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]">
<header>
<h1 v-text="guide.title.rendered" />
</header>
</article>
</template>
isGuideVisible should be a method returning whether the item is rendered, so you don't have to write that logic inside your markup. One advantage of this method is that you can follow your v-if element with a fallback v-else element, should you want to replace the missing items with fallback content. Just remember to also :key="index" the fallback element as well.
Apart from the above use-case, <template> tags come in handy when rendering additional wrapper elements is not an option (would result in invalid HTML markup) (i.e: table > tr > td relations or ol/ul > li relations).
It's mentioned here as "invisible wrapper", but it doesn't have a dedicated section in the docs.
Side note: since you haven't actually shown what's inside guide.categories, I can't advise on it, but there's probably a cleaner way to deal with it than .toString().replace(). If guide.categories is an array of strings, you could simply go: :class="guide.categories".
I think the most Vue way is to create a computed property with filtered items from selected categories, then use that in v-for loop (the idea is to move the business logic away from template).
computed: {
filteredItems(){
return this.guides.filter(e => this.selectedCategories.includes(e.category))
}
}
Also, as a note, it is not recommended to use v-if and v-for on the same element, as it may interfere with the rendering and ordering of loop elements. If you don't want to add another level of nesting, you can loop on a 'template' element.
<template v-for="item in items">
// Note the key is defined on real element, and not on template
<item-element v-if='condition' :key="item.key"></item-element>
</template>

vuejs expression is not working correctly

what is wrong with my expressions? I am making a rest api call, which works fine. Data loads and gets written to console. I am simply trying to get that data on screen. I can but its not correct. It either shows all of the data. I am only trying to get the first item in the array. items.Name does not work, but it does if I do a v-for="item in items". Regardless it loads everything. When I add a [0] to get the first element, it simply removes all the text except the first letter of the object in the array.What do I need to do?
<p>{{items.Name}}</p>
Please, read the docs about list rendering
https://v2.vuejs.org/v2/guide/list.html
If you have an items array of objects, then you can loop over it in your HTML by using v-for, or directly access each entry by specifying the index in the expression: {{ items[0].Name }}
<ul>
<li v-for="item in items" :key="item.ID">{{ item.name }}</li>
</ul>
Notice you're using item, not items, inside the <li> tag, because you instructed v-for to assign the cursor to that variable.

Add custom fields for use in template

Currently custom fields show up as text on product pages.
Custom Field #1 Name: Material, Custom Field #1 Value: Cotton
Is there some other option instead to just pass data to the template? For example I'd like to display a 'NEW' label on the product page if new == true.
Sort of like https://springmerchant.com/bigcommerce/product-labels/
Right now we're using handlebars and if-statements to hide custom fields with a __prefix. For example __new: true.
If you're developing your template in stencil, there are a couple of options... You can loop through the custom fields until you find the correct one and then check the value... Ex:
{{#each product.custom_fields}}
{{#if name "==" "__new"}}
{{#if value "==" "true"}}
YOUR HTML CODE HERE
{{/if}}
{{/if}}
{{/each}}
Alternatively, you can put all your custom_fields into an array and use javascript to populate various aspects of the site:
<script>
var custom = [];
{{#each product.custom_fields}}
custom.push({'name':"{{name}}",'value':"{{value}}"});
{{/each}}
YOUR CODE TO LOOP THROUGH JSON ARRAY AND DO VARIOUS TESTS AND STUFF
</script>
If you don't have access to stencil templates and are just doing development through the control panel, you could write javascript to parse the default custom_field html and then use the data accordingly.
None of the solutions are particularly clean, but they all work.

Selenium - Search for an element within element

Hi I want to hold element references in files somewhere. and then in run time search for elements withing referenced elements in Selenium how to do that.
For example- a Frame contains multiple text boxes -and multiple frames of similar properties exist where the textboxes are also duplicate. Something like I wanna reference the text box under a particular frame. But i wanna predefine the frame. and the specify that search under that frame[Something like Aliases in Testcomplete]
For Example - similar concept exist in Cheezy's Page-Objects. but not quite.
if you have a structure like this:
<div class='some class'>
<input class='input-button' value='submit'>Submit</input>
</div>
<div class='some class2'>
<input class='input-button' value='submit'>Submit</input>
</div>
and you want to find the first 'Submit' which is within the 'some class' div, you can do this:
parent_element = driver.find_element(:xpath, "//div[#class='some class']")
child_element = parent_element.find_element(:xpath, ".//input")
p.s. this is ruby code.