Aurelia custom element - $parent undefined - javascript

I have template with repeater:
<template repeat.for="i of 2">
<template repeat.for="j of 2">
<p>${ $parent.$index } - ${ $index }</p>
</template>
</template>
Which prints result:
0 - 0
0 - 1
1 - 0
1 - 1
If I use custom element child-item with the same template:
<template>
<p>${ $parent.$index } - ${ $index }</p>
</template>
And write my original example using child-item:
<template repeat.for="i of 2">
<child-item repeat.for="j of 2"></child-item>
</template>
Result is only:
-
-
-
-
Is there a way to propagate $parent and $index transparently to the child-item?
UPDATE
After trying few suggestions, closest I came is this:
<template repeat.for="i of 2">
<child-item repeat.for="j of 2" parent-index.bind="$parent.$index" index.bind="$index"></child-item>
</template>
Where child-item template looks like:
<template bindable="parentIndex, index">
<p>${ parentIndex } - ${ index }</p>
</template>
Binding $parent context directly with parent.bind="$parent" does not work. Parent index has to be bound directly. With this approach anything inline with $parent.$parent.$index is not achievable.

something like this will work
child-item.ts:
import { customElement, bindable } from 'aurelia-framework';
#customElement('child-item')
export class ChildItem {
#bindable index;
}
child-item.html
<template>
<p>${ index }</p>
</template>
template with repeater:
<template>
<require from="./child-item">
<child-item repeat.for="child of childred" index.bind="$index"></child-item>
</template>

You would need to use databinding to pass it in. Add a parent or index bindable property to the child-item viewmodel.

Related

child templates in custom component v-data-table

I am creating a general component based on v-data-table. This component has templates that are displayed anywhere, such as: ..template v-slot:item.index="{ item }"> ....
The idea is to be able to pass custom templates as children of the component, in this case I would like to pass "..template v-slot:item.state="{ item }"> ..."
As you can see, in the source code of the ..[DataTableFuntional>] <v-data-table... component there is a commented template (..template v-slot:item.state="{ item }">) which works perfectly.
But it does not suit my need since that way I would have to pass props to activate or deactivate the template because not all tables would always carry that field called "state"
DataTableFuntional component
<template>
<div>
<v-data-table
class="tableBackground"
:dense="dense"
:headers="headers"
:items="items.data"
:options.sync="options"
no-data-text="No hay datos disponibles"
:loading-text="$t('comunes.general.cargando')"
>
<!-- Custom templates -->
<template v-slot:body.append>
<slot name="body2"></slot>
</template>
<template v-slot:item.state="{ item }">
<slot name="state"></slot>
</template>
<!-- <template v-slot:item.state="{ item }">
<div class="">
<span v-if="item.state === 1" color="red">Close</span>
<span v-else-if="item.state === 2" color="green">Open</span>
</div>
</template> -->
<!-- Default templates -->
<template v-slot:item.index="{ item }">
{{ items.data.indexOf(item) + 1 }}
</template>
</v-data-table>
</div>
</template>
Now, if we see this code, which is the previous component imported in any page of the site.
We see that there are two "slots": v-slot: state" and "v-slot: body2" that are shown correctly, but if we see the code of the v-data-table> ... we can see that it is necessary to specify the v -slot in the template, for example:
<template v-slot: item.state = "{item}">
<slot name = "state"> </slot>
</template>.
now, if we see the first tamplate:
<template v-slot: item.state = "{item}">
<div class = "">
<span v-if = "item.state === 1" color = "red"> Close </span>
<span v-else-if = "item.state === 2" color = "green"> Open </ span
>
</div>
</template>
This would be the way I would more or less like to be able to assign new templates to the v-data-table component.
but it does not work.
if we see the following template
<template v-slot: state>
{{'Hello'}}
</template>
This does work but I don't know how to access the item to be able to set the conditions according to the state.
imported DataTableFuntional component whit child templates
<DataTableFuntional
:ref="'reference'"
:endpoint="item.endpoint"
:headers="item.headers"
:actions="item.actions"
:btsmall="true"
>
<!-- Custom templates -->
<template v-slot:item.state="{ item }">
<div class="">
<span v-if="item.state === 1" color="red">Close</span>
<span v-else-if="item.state === 2" color="green">Open</span
>
</div>
</template>
<template v-slot:state>
{{'Hello'}}
</template>
<template v-slot:body2>
<tr>
<td></td>
<td>
<v-text-field
type="number"
label="Less than"
></v-text-field>
</td>
<td colspan="4"></td>
</tr>
</template>
</DataTableFuntional>

Passing slot to grandchild though dynamic component

I have a wrapper component(Wrapper.vue).
<component v-for="c of comps" :key="c.componentName" :is="c.componentName">
<slot :name="c.componentName">
</slot>
</component>
const comps =
[
{
componentName: "Child1",
},
{
componentName: "Child2",
}
];
Child1.vue is something like below
<template>
<div class="hello">
<slot name="top">Child1 slot top text</slot>
</div>
</template>
Child2.vue is something like below
<template>
<div class="hello">
<slot name="top">Child2 slot top text</slot>
</div>
</template>
I want to use the wrapper like below in App.vue
<Wrapper>
<template slot="slot1">
<template slot="top">Inner Content1</template>
</template>
<template slot="slot2">
<template slot="top">Inner Content2</template>
</template>
</Wrapper>
CodeSandbox Link:- https://codesandbox.io/s/mystifying-burnell-ir8un
It is showing default text from Child1 and Child2 not from App.vue
What should be done to fix it?
I changed some code and this is the result. The changes I made:
you had nested the <template> tag inside App.vue.
the wrapper itself needed to have a template for slot named 'top' and inside this template I added a slot with the same name of the component
https://codesandbox.io/s/lucid-smoke-uzgn7?file=/src/App.vue

How to extend a Vue component with slots

I want to get a third party library component, add one element more and use this third party with the same way as always.
Example:
<third-party foo="bar" john="doe" propsFromOriginalLibrary="prop">
<template v-slot:top=v-slot:top={ props: test }>
Some text on third-party component slot
</template>
</third-party>
Want to code as:
<my-custom-component propsFromOriginalLibrary="prop">
<template v-slot:top={ props: test }>
Some text on third-party component slot
</template>
</my-custom-component>
And both examples work the same way. I was able to get all the props by using:
<third-party v-bind="$attrs">
but not sure about how to handle the slots
Here is how you can do it:
my-custom-component template:
<template>
<third-party v-bind="$attrs">
<template v-slot:top="slotProps">
<slot name="top" v-bind="slotProps"></slot>
</template>
</third-party>
</template>
What's happening:
Inserting the third party component and v-bind $attrs
Referencing its top slot
Custom component has a slot which is passed into third's slot
Custom slot has the same name so it can be v-slot the same way from a parent
Custom slot v-binds all 3rd party slotProps to pass out to a parent
You can use a v-for to avoid the need for hard-coding an inner template for each slot. For example if you wanted to expose two slots, top and bottom:
<template>
<third-party v-bind="$attrs">
<template v-for="slot in ['top','bottom']" v-slot:[slot]="slotProps">
<slot :name="slot" v-bind="slotProps"></slot>
</template>
</third-party>
</template>
Demo:
Vue.component('third-party', {
props: ['background'],
template: `
<div class="third" :style="{ background }">
3rd party slot:
<div class="third-slot">
<slot name="top" :props="props"></slot>
</div>
</div>
`,
data() {
return {
props: 'Third party Prop'
}
},
})
Vue.component('my-custom-component', {
template: `
<div>
<component is="third-party" v-bind="$attrs">
<template v-for="slot in ['top']" v-slot:[slot]="slotProps">
<slot :name="slot" v-bind="slotProps"></slot>
</template>
</component>
</div>
`
})
/***** APP *****/
new Vue({
el: "#app"
});
.third,.third-slot {
padding: 10px;
}
.third-slot {
background: #cccccc;
border: 1px solid #999999;
font-weight: bold;
}
<script src="https://unpkg.com/vue#2.6.12/dist/vue.js"></script>
<div id="app">
<third-party background="#eeeeff">
<template v-slot:top="{ props: test }">
Some text on third-party component slot
<div>{{ test }}</div>
</template>
</third-party>
<my-custom-component background="red">
<template v-slot:top="{ props: test }">
Some text on third-party component slot
<div>{{ test }}</div>
</template>
</my-custom-component>
</div>
Fun: You could even make the wrapped component dynamic like <component :is="thirdpartyName"> and the slot name array too; even passing this info in from outside for a fully generic wrapper. But there's no need for that here

How to make dynamic object properties available for parent component inside v-for in child one

I have a child component that uses v-for. Here is the child component:
<template>
<div>
<ul>
<li v-for="item in listItems"
:key=item.id>
<span>{{item.name}} - {{item.color}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
listItems: Array
}
};
</script>
listItems is an Array of objects.
My question is: How do I make the property names in between the <span> tags dynamic from the parent component? Depending on the array of objects passed into the props as listItems, sometimes I may want the text in the <span> tags to be different based on the properties of the objects in the array. For example:
<span>{{item.id}} - {{item.location}}</span>
You could use scoped slots as follows :
<li v-for="item in listItems"
:key=item.id>
<slot v-bind:item="item">
<span>{{item.name}} - {{item.color}}</span>
</slot>
</li>
then you use it as you like :
<child>
<template v-slot:default="{item}">
<span>{{item.id}} - {{item.location}}</span>
</template>
</child>
or
<child>
<template v-slot:default="{item}">
<p>{{item.location}}</p>
</template>
</child>
If you only have a few different ones you're using, you could have a series of <span v-if="item.id">{{item.id}}</span> - <span v-if="item.foo">{{item.foo}}</span>

Loop variables in Vue.js similar to AngularJS

I my vue.js application I want to know the last repeated element in a v-for directive. I know there is something similar in angularjs with the $last loop variable in its ngRepeat directive:
<div ng-repeat="(key, value) in myObj">
<span ng-if="$last">Shows if it is the last loop element</span>
</div>
Is there any equivalent in vue.js which I am not aware of or do I have to implement some own logic?
It works for me :)
<div v-for="(item, index) in items">
<span v-if="index === (items.length-1)">This is the last loop element</span>
</div>
you can also put the logic in a computed property :
<div v-for="(value, key) in myObj">
<span v-if="key === last">This is the last loop element : {{ value }}</span>
</div>
//...
computed: {
last() {
let keys = Object.keys(this.myObj)
return keys.slice(-1)[0]
}
}
fiddle here
There's no equivalent for this in Vue, no.
You can do this instead, check for index if it is last
<div v-for="(value, key, index) in myObj">
<div v-if="index === Object.keys(myObj).length-1"> my content</div>
</div>
You can try this
<div v-repeat="myObj">
<span v-if="$index === (myObj.length-1)">Shows if it is the last loop element</span>
</div>
Based on #Nisha answer I have been doing this:
<template v-for="(obj, index) in originalData">
<template v-if="index == 0 || (index >= 1 && obj.valueA !== originalData[index - 1].valueA)">
{{ obj.valueA }}
</template>
</template>
I want the loop to always happen the first time, but to conditionally check a value for every loop afterwards. I am using <template> tags to avoid outputting unnecessary markup. In my example originalData is an array of objects.

Categories