Working with sub-objects in handlbars - templates

How does one work with sub-objects in handlebars? I have the following data:
{
name: "first-name-field",
spec: {
label: {
text: "First Name:"
},
input: {
type: "text",
value: ""
}
},
template: "field"
}
The field template it is referring to is as follows:
<div class="label-wrapper">
<label>
{{specs.label.text}}
</label>
</div>
<div class="input-wrapper">
<input type="{{specs.input.type}}" value="{{specs.input.value}}" />
</div>
The label section builds but the input section never appears. Im assuming this is because the scope has already changes. I have tried the ../ prefix as well, but it never sees to move back to the parent object.

In your code you have done some spell mistake. In template "specs" and in dict "spec"
Otherwise your code is working fine. I have tested your code screen short is attached.

Related

Vuejs, removing element from nested lists

This is a followup question on VUEJS remove Element From Lists?, where they give various methods (this.$remove, splice, this.$delete) for dynamically removing a element from a list. I was trying to understand how to apply this to a nested loop; here's mine in three+ levels, somewhat stripped-down:
<template v-for="(labtype,index) in labIRlist">
<template v-for="(lab,index2) in labtype">
<tr v-for="(IR,index3) in lab.irs" :key="IR.irn">
<td><p>{{ lab.hidtxt }}_{{ lab.mnem }}</p></td>
<td><p>{{ lab.PNL }}</p></td>
<td><p>{{ IR.provider}} {{ IR.psurv }}</p></td>
<td><p>{{ IR.year }}-{{ IR.eventno }}</p></td>
<td><p>{{ IR.analytes }}</p></td>
<td><p>
<button type="button"
#click="deleteIR(IR.irn,index,index2,index3)">
DELETE
</button>
</p></td>
</tr>
</template>
</template>
Then there's Javascript for the deletion
methods: {
deleteIR: function(IRNum,index,index2,index3) {
// okay, delete!
//... code to do something at the database...
alert('IR successfully deleted!')
// don't show the deleted IR any more
this.labIRlist[index][index2].irs.splice(index3,1);
},
Awful, but I didn't know how to identify the correct element. And it still didn't work (AFAIK it does nothing, no change visible to the row). How should this be done - don't we know the right element from where deleteIR was called?
What is the better way to handle deleting an element within multiple
loops?
First of all, looking at how your template is built, we can assume that your data looks something like this:
labIRlist: [
[
{
hidtxt: "TEXT 1",
irs: [{ provider: "provider1" }, { provider: "provider11" }],
irn: "irn1",
},
{
hidtxt: "TEXT 2",
irs: [
{ provider: "provider2" },
{ provider: "provider22" },
{ provider: "provider222" },
],
irn: "irn2",
},
],
[
{
hidtxt: "TEXT 3",
irs: [{ provider: "provider3" }],
irn: "irn3",
},
],
],
If you want to delete one item from the nested irs array, all you need to do is to pass the irs array and the index of the item you want to delete, no need to do the whole path you are doing currently:
this.labIRlist[index][index2].irs.splice(index3,1);
So change your template to:
<button type="button" #click="deleteIR(lab.irs, index3)">
And the delete function is then simply:
deleteIR(IRS, index) {
IRS.splice(index, 1);
}
A DEMO for your reference
PS. Please always share a bit of your data when posting questions like this. Much easier to answer when not needing to create your own sample data like I did here.

ember-bootstrap - rendering "radio" inputs

Using the Ember addon ember-bootstrap I can make a set of radio buttons like this :
{{form.element controlType="radio" label="Fruit Type" property="radio" options=radioOptions optionLabelPath="label"}}
with a Controller that looks like this :
export default Controller.extend({
init() {
this._super(...arguments);
this.radioOptions = [
{
label: 'Citrus',
value: 'C',
inline: true
},
{
label: 'Non-Citrus',
value: 'N',
inline: true
}
];
}
});
The relevant doco is here https://www.ember-bootstrap.com/#/components/forms .
However what I can't do is provide a custom value to each radio button so that I end up with rendered HTML like this :
<label>Citrus</label>
<input type="radio" value="C">
<label>Non-Citrus</label>
<input type="radio" value="N">
I have looked at "Custom Controls" on https://www.ember-bootstrap.com/#/components/forms but I can't see how that applies to this case.
EDIT: Just to be clearer about why I want to do this I want to display the readable label (eg "Citrus") but have the non-readable value ("C") available to send back to the server (because the server thinks in terms of "C" or "N".
It's not essential I could send "Citrus" back and map it around on the server but I just thought this would be very straightforward.
Looking at the part of the doco starting with "You can also just customize the existing control component:" on https://www.ember-bootstrap.com/#/components/forms it does seem like you should be able to do the sort of thing I'm after but the example shown doesn't address the use of a value attribute and I can't figure out how to .
You don't need to have the HTML rendered like that. if you want to access the checked radio, simply it is the property name dot value like radio.value.
Here how to get it in the on submit action:
actions: {
onSubmit() {
alert(this.radio.value)
}
}
I had exactly the same issue but I finally solved it. You have to use form.element in block mode. Why is it also necessary to also write an action to update the value? I have no idea!
In my implementation, I'm using Ember changesets and my property is called usageType. I hope it's clear enough to adapt for your needs.
I'm also using Ember Truth Helpers, which is what the {{eq opt.value el.value}} part is. It sets checked to true if the input value is equal to the current-selected value.
# my-component.js
actions: {
selectUsageType(option) {
return this.set('changeset.usageType', option);
}
}
# usage-type-options.js (imported in component JS)
[{
label: 'First label',
value: 'A'
},
{
label: 'Second label',
value: 'B'
}]
# Etc.
# my-component.hbs
# I'm not using angle-bracket invovation here, oops
{{#form.element
property="usageType"
label="My label" as |el|
}}
{{#each usageTypeOptions as |opt|}}
<div class="form-check">
<input
type="radio"
class="form-check-input"
id="{{el.id}}-{{opt.value}}"
checked={{eq opt.value el.value}}
onchange={{action "selectUsageType" opt.value}}
>
<label for="{{el.id}}-{{opt.value}}" class="form-check-label">
{{opt.label}}
</label>
</div>
{{/each}}
{{/form.element}}

Nuxt JS Apollo data only available after page refresh

I am fetching some data using Apollo inside of Nuxt. Somehow, when navigating to that page I get an error of
Cannot read property 'image' of undefined
When I refresh the page, everything works as expected.
I have a found a few threads of people having similar issues but no solution seems to work for me :/
This is my template file right now:
/products/_slug.vue
<template>
<section class="container">
<div class="top">
<img :src="product.image.url"/>
<h1>{{ product.name }}</h1>
</div>
</section>
</template>
<script>
import gql from 'graphql-tag'
export default {
apollo: {
product: {
query: gql`
query Product($slug: String!) {
product(filter: { slug: { eq: $slug } }) {
slug
name
image {
url
}
}
}
`,
prefetch({ route }) {
return {
slug: route.params.slug
}
},
variables() {
return {
slug: this.$route.params.slug
}
}
}
}
}
</script>
Basically the $apolloData stays empty unless I refresh the page. Any ideas would be much appreciated
EDIT
Got one step closer (I think). Before, everything (image.url and name) would be undefined when navigating to the page for the first time.
I added:
data() {
return {
product: []
};
}
at the top of my export and now at least the name is always defined so if I remove the image, everything works as expected. Just the image.url keeps being undefined.
One thing I noticed (not sure how relevant) is that this issue only occurs using the , if I use a normal a tag it works but of course takes away the vue magic.
EDIT-2
So somehow if I downgrade Nuxt to version 1.0.0 everything works fine
I stumbled on this issue as well, and found it hidden in the Vue Apollo documents.
Although quite similar to the OP's reply, it appears the official way is to use the "$loadingKey" property.
It's quite confusing in the documents because there are so many things going on.
https://vue-apollo.netlify.com/guide/apollo/queries.html#loading-state
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables: {
slug: "about"
}
},
}
}
</script>
If you need to use a reactive property within vue such as a slug, you can do so with the following.
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables() {
return {
slug: this.$route.params.slug
}
}
},
}
}
</script>
I think it's only a problem of timing on page load.
You should either iterate on products, if you have more than one, or have a v-if="product != null" on a product container, that will render only once the data is fetched from GraphQL.
In that way you'll use the object in your HTML only when it's really fetched and avoid reading properties from undefined.
To fix this, you add v-if="!$apollo.loading" to the HTML container in which you're taying to use a reactive prop.

JsViews: in-line template syntax for direct linked form element

I saw an example of linking directly to a form element using JsViews, which I found to be preferable to encapsulating the whole form in a template. Here is a jsfiddle example of what I'm trying to do, which partially works:
http://jsfiddle.net/30jpdnkt/
var app = {
selections: {
things: [
{ Name: "thingName1", Value: "thingValue1" },
{ Name: "thingName2", Value: "thingValue2" },
{ Name: "thingName3", Value: "thingValue3" }
]
},
formData: {
selectedThing: "thingValue1",
}
};
//how do I reference this template in-line, outside of another wrapping template?
$.templates({
theTmpl: "#theTmpl"
});
$("#content").link(true, app);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://www.jsviews.com/download/jsviews.js"></script>
<script id="theTmpl" type="text/x-jsrender">
<select id="thingChoice" data-link="formData.selectedThing">
<option value="-">Please select</option>
{^{for selections.things}}
<option data-link="value{:Value} {:Name} selected{:~selected === Value}"></option>
{{/for}}
</select>
</script>
<div id="content">
<!--this part works-->
<input data-link="formData.selectedThing trigger=true"/>
<!--this part does not display-->
<span {{for data tmpl="theTmpl"/}}></span>
</div>
The data-linked INPUT tag is correctly bound to the object, but I cannot find a working example of how to reference a compiled template in-line without encapsulating the entire form in another template. That it's possible to use data-link syntax outside of a template gives hope that it may be possible with correct syntax.
Is this possible?
Yes it is possible - it is what I call top-level data-linking. There will be new documentation topics on this coming very soon, but meantime you have this sample:
http://www.jsviews.com/#samples/editable/toplevel-for
And your jsfiddle - which I updated to make it work fully: http://jsfiddle.net/30jpdnkt/1/
<div id="content">
<input data-link="formData.selectedThing trigger=true"/>
<span data-link='{for tmpl="theTmpl"}'></span>
</div>

Windows 8 JS & html5 app - foreach within template

How do I loop thru items in a WinJS.Binding.Template within my WinJS.UI.ListView control?
My data:
{ category: 'Sports',
items: [
{ title: 'soccer'}, { title: 'tennis'}
]
}
What I want to do in my template:
<div id="myTmpl" data-win-control="WinJS.Binding.Template" style="display:none">
<h1 data-win-bind="innerText: category"></h1>
<div data-win-REPEATER="each: items">
<span data-win-bind="innerText: title"></span>
</div>
</div>
Because you want to nest the details of the items, I would recommend that you look at my answer to another question, which is very similar:
WinJS ListView and Template Binding
This has all the essential details.
That stated, theres no reason you cant integrate JQuerys templates here - you just need to find a way for them to work with the WinJS template / control contract.
Have you checked
http://code.msdn.microsoft.com/windowsapps/ListView-basic-usage-sample-fcc451db
Normally the loop is automatically (it will do based on the datasource information)
<div id="listView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource: myData.dataSource,
itemTemplate: myTmpl,
selectionMode: 'none',
tapBehavior: 'none',
swipeBehavior: 'none',
layout: { type: WinJS.UI.GridLayout }
}"
></div>