Slots are not showing - Vue.js - javascript

I'm learning vue right now and have problems with understanding the slots.
I got two components:
BaseIcon
<template>
...
<slot name="test"></slot>
...
<template/>
EventCard
<template>
<router-link class="event-link" :to="{ name: 'event-show', params: {id: '1'}}">
..
<BaseIcon name="users" slot="test">{{ event.attendees.length }} attending</BaseIcon>
..
</router-link>
</template>
But the the "slot" ain't replaced with the content in the BaseIcon component tags in EventCard.

You could use it like v-slot because slot syntax is deprecated as mentioned here:
<BaseIcon name="users">
<template v-slot:test>
{{ event.attendees.length }} attending
</template>
</BaseIcon>
or a shorthand :
<BaseIcon name="users" >
<template #test>
{{ event.attendees.length }} attending
</template>
</BaseIcon>

I had a very similar problem recently. Turned out there was a tiny glitch in my HTML markup (I hadn't closed a bold tag). Presumably, the Vue routine that converts template HTML into Javascript script is super sensitive to this kind of glitch (unlike browsers which have been brilliant at coping with them for years) - so the template was silently failing.
I had to use a laborious "divide and conquer" process to track it down - chop everything out, then paste it back bit by bit. But there may be syntax checkers out there that would analyse your template markup thoroughly.
Worth checking.

The named v-slot can be used only in the template. Default can be used in the component too. See the docs: https://v2.vuejs.org/v2/guide/components-slots.html#Abbreviated-Syntax-for-Lone-Default-Slots
Also:
Note that v-slot can only be added to a (with one exception), unlike the deprecated slot attribute.
https://v2.vuejs.org/v2/guide/components-slots.html#Named-Slots

Related

How can I output the <template /> tag to the DOM in Vue.js

I have a custom library built on Stencil that I need to integrate with Vue, and this library relies on the browser's <template /> tag.
For example, I need to render the following code to the dom:
<my-custom-list>
<template>
<my-custom-title></my-custom-title>
<my-custom-description></my-custom-description>
</template>
</my-custom-list>
my-custom-list will then get whatever is inside the template and use it for each of the items in the list.
I managed to get it to work by using the v-html directive, for example:
<my-custom-list v-html="<template><my-custom-title></my-custom-title><my-custom-description></my-custom-description></template>">
</my-custom-list>
However, I would also like to be able to use Vue components inside this template tag, like:
<my-custom-list>
<template>
<my-custom-title></my-custom-title>
<my-custom-description></my-custom-description>
<custom-vue-component></custom-vue-component>
</template>
</my-custom-list>
Some old answers online suggest using the is directive to be able to output the template tag, like <div is="template"> but that has been deprecated.
I am not even sure if this is possible, but any insights would be much appreciated.
You can use the v-pre directive to skip compilation for parts of your components.
<my-custom-list v-pre>
<template>
<my-custom-title></my-custom-title>
<my-custom-description></my-custom-description>
<custom-vue-component></custom-vue-component>
</template>
</my-custom-list>

What do the hash marks (#) mean in Vue?

I am a rather new Vue developer.
Everywhere I go in our stack, I see code like this inside our components components:
<template #item.active="{ value }">
<div :aria-label="String(value)" class="text-center">
<v-icon v-if="value === null">mdi-minus</v-icon>
<v-icon v-else color="red">mdi-close</v-icon>
</div>
</template>
And for the life of me, I am cannot figure out what the #item.active (specifically the #) actually does. We have many hashed items. Like <template #item.actions-prepend="{item}"> or <template #toolbar-extension>
Googling a # isn't an easy thing to do.
And apparently I missed this specific video in my Vue tutorials!
We use Nuxt and Vuetify, not sure if that helps!
As mentioned in the comments, the # symbol is a shorthand for the v-slot attribute, as hinted by the usage of <template> (which v-slot only allows to be used on, as well as components) in your code.

Conditional scoped slot templates in vue

Can I not do something like this?
<template v-if="options.columns">
one thing...
</template>
<template v-else slot-scope="scope">
something else. scope doesn't work here. :(
</template>
When I try to do this, scope becomes undefined. The moment I remove v-if, else and use scope it works as expected.
I was facing with the exact same problem, what's worse, mine is failing silently - I didn't see any errors.
Solution #1 move if else condition to the inner scope of <template>
As David Japan pointed out, you can check if else condition in the inner scope of slot-scope:
<template slot-scope="scope">
<span v-if="options.columns">one thing...</span>
<span v-else>something else.</span>
</template>
Solution #2 use v-slot instead of slot-scope
<template v-slot="scope" v-if="options.columns">
one thing...
</template>
<template v-slot="scope" v-else>
something else.
</template>
However I don't know why v-slot fixes it, I searched both in the official documentation and online but no clues.
The solution is a workaround:
Create 2 different SFC for each possible use case and do conditional rendering.
Vuetify example:
// App.vue
<div>
<ListMD v-if="this.$vuetify.breakpoint.name === 'md'" />
<ListXS v-if="this.$vuetify.breakpoint.name === 'xs'" />
</div>
In Vue.js component you cannot have more than one template tag.
Why not using 'is' property for dynamic component ?
<template>
<div :is='my-component'></div>
</template>
So you can load a component dynamicaly.
https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components

Using custom element content as item template

I'm writing reusable components for our internal framework that abstract away some monkey code. Most of the scenario's are implemented with slots and work great. However, some scenario's require rendering templates inside for loops, and unfortunately slots aren't supported there.
I came up with the following (working) code:
<template>
<div class="form-group">
<label for.bind="titleSafe" class="control-label">{title}</label>
<select id.bind="titleSafe" value.bind="value" class="form-control">
<option repeat.for="item of itemsSource" >
<template replaceable part="item-template" containerless>${item}</template>
</option>
</select>
</div>
</template>
This code has, IMO, multiple issues that make it a bad candidate for including it in a framework:
It doesn't support default templates like slots does, so when you have only 1 replacable part the syntax is needlessly verbose
Having to use 2 different templating systems (slots + replace-part) in my project seems really counter intuitive and will certainly create confusion/bugs in my dev team
When you use template parts in the example I provided above, you need to know I declared 'item' as iterator in my for loop in order to construct your template correctly
Therefore I went looking for alternatives. After some research I came up with something like this:
<template>
<div class="form-group">
<label for.bind="titleSafe" class="control-label">{title}</label>
<select id.bind="titleSafe" value.bind="value" class="form-control">
<option repeat.for="item of itemsSource" >
<!-- I want to insert my custom element here -->
</option>
</select>
</div>
<slot></slot>
</template>
The above is my select-item custom element. Then I would also create another custom element for the templating of the repeatable item, like select-item-template, I would then use the two together like this:
<select-item title="myTitle" items-source="myItems">
<select-item-template><span>${myItemsProperty}</span></select-item-template>
</select-item>
The strength of this approach would be that you can create complex 'root' custom elements with one default slot. In this slot, you could then define multiple custom 'child' elements that the root element can search for when it's initialized (I know you can do this with the #child and #children decorators, so that part is covered). I'm a bit lost on how I would have to use these custom child element's content in my root custom element though.. How would I take my span element in the above example and prepare it's content to be rendered in the repeater? And would it be possible to take the repeated item set it as the template's datasource so I don't have to specify itemin my templates?
I hope I didn't make this too verbose, but I wanted to explain what my functional requirement is. If you have any resource that can point me in the right direction I would be very grateful!
Use the processContent attribute to transform the element content into a part replacement. The component will still use replace-part internally but consumers of the component won't be exposed to this implementation detail.
https://gist.run?id=2686e551dc3b93c494fa9cc8a2aace09
picker.html
<template>
<label repeat.for="item of itemsSource" style="display: block">
<input type="radio" value.bind="item" checked.bind="value">
<template replaceable part="item-template">${item}</template>
</label>
</template>
picker.js
import {bindable, processContent} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding';
import {FEATURE} from 'aurelia-pal';
#processContent(makePartReplacementFromContent)
export class Picker {
#bindable itemsSource = null;
#bindable({ defaultBindingMode: bindingMode.twoWay }) value = null;
}
function makePartReplacementFromContent(viewCompiler, viewResources, element, behaviorInstruction) {
const content = element.firstElementChild;
if (content) {
// create the <template>
const template = document.createElement('template');
// support browsers that do not have a real <template> element implementation (IE)
FEATURE.ensureHTMLTemplateElement(template);
// indicate the part this <template> replaces.
template.setAttribute('replace-part', 'item-template');
// replace the element's content with the <template>
element.insertBefore(template, content);
element.removeChild(content);
template.content.appendChild(content);
return true;
}
}
usage
<template>
<require from="picker"></require>
<h1>Default Item Template</h1>
<picker items-source.bind="colors" value.bind="color"></picker>
<h1>Custom Item Template</h1>
<picker items-source.bind="colors" value.bind="color">
<em css="color: ${item}">
${item}
</em>
</picker>
</template>

How do I pass template variables in Meteor?

I couldnt find an answer for this but I've seen it before.
I want to render a template with a varable set from html and have access to it in js as well. Here's a simple example that covers both of these cases.
<template name="a">
{{>b param="hello"}}
</template>
<template name="b">
{{param}} {{param2}}
</template>
Template.b.param2 = function() {
if (this.param == "hello") {
return "world"
}
}
This doesnt seem to work though.
EDIT:
Well that all works apparently. I didnt include the #each:
<template name="a">
{{>b param="hello"}}
</template>
<template name="b">
{{#each something}}
{{param}}
{{/each}}
</template>
I think thats what's causing the problem. (sorry this example is a little contrived)
So this was king of a bad question but maybe it will save someone some trouble.
For the #each issue, the solution is to do {{../param}} which I found in another question.
However, this didnt work for me because I am using Autoform in which case I actually had to do {{../../param}}.

Categories