Named <slot> elements can't receive <template> elements - polymer

I have a little problem with the <slot/> element.
From Polymer version >= 1.8 <slot/> elements should be used, instead of <content/>, for creating insertion points. But a <slot/> cannot receive a <template/> element in any way, when it has a name.
When I create an unnamed (default) slot in my container element:
<dom-module id="my-container">
<template>
<slot id="defaultSlot"></slot>
</template>
</dom-module>
and nest a plain template:
<my-container id="container">
<template>
<div>foo</div>
</template>
</my-container>
it works perfectly, which can be verified using:
> container.$.defaultSlot.getDistributedNodes().length↵ -> 1
However, slotting of template elements stops working as soon as I give that slot a name:
<dom-module id="my-container">
<template>
<slot name="bar" id="namedSlot"></slot>
</template>
</dom-module>
<my-container id="container">
<template slot="bar">
<div>foo</div>
</template>
</my-container>
> container.$.namedSlot.getDistributedNodes().length↵ -> 0
What i really need is to make <slot/> somehow accept any <template/> by slot name.
Working Fiddle.

Related

Vue Error: "the template root requires exactly one element"

I have the following components
component
<template>
<!--<post-form/>-->
<div class="wrapper">
<b-table :data="posts">
<template slot-scope="props">
<b-table-column field="completed" label="complete">
<b-checkbox
v-model="props.row.done">
</b-checkbox>
</b-table-column>
</template>
</b-table>
</div>
</template>
I have a b-table with buefy. And I want to import the <post-form /> component from the current component.
However, when I insert the component in the desired location, an error occurs.
<div class="wrapping"> If I place a component under it, my table is broken.
How can I solve this?
Maybe you are using Vue3, while you eslint config is still vue 2.
Try to edit your .eslintrc.json or something like this:
{
// ...
"extends": ["eslint:recommended", "plugin:vue/vue3-essential"],
// ...
}
Check out reference here: eslint-plugin-vue/#usage
We can only have one root element in the template. So, if you want to use as a sibling of div with class wrapper, you need to wrap both of these to a parent div, as below:
<template>
<div>
<post-form/>
<div class="wrapper">
<b-table :data="posts">
<template slot-scope="props">
<b-table-column field="completed" label="complete">
<b-checkbox
v-model="props.row.done">
</b-checkbox>
</b-table-column>
</template>
</b-table>
</div>
</div>
</template>

How to insert template tag inside another template Vue.js

I'm trying to make a component that has a slot with a name attribute and another component with the same structure. So, I want to put the second component inside the first but in another outside component.
Example:
App.vue
<template>
<ComponentA>
<ComponentB>Hello World!</ComponentB>
</ComponentA>
</template>
ComponentA.vue
<template>
<div id="A">
<slot name="a1"></slot>
<slot name="a2"></slot>
</div>
</template>
ComponentB.vue
<template slot="a2">
<div id="B">
<slot></slot>
</div>
</template>
UPDATE!
codesandbox
If I write
<slot></slot>
instead of
<slot name='a2'></slot>
in ComponentA it will work. But I need use the last form, because I must control the order.

nesting elements and dynamic content inside dom-repeat - Polymer 1.0

I have a <parent> element, a <tabs> element inside it with an arbitrary number of tabs (purely for hiding/showing logic in the UI), and a <child> element inside each <tab>. Right now I have the following working:
<!-- inside parent.html: -->
<tabs></tabs>
<!-- inside tabs.html: -->
<template is="dom-repeat" items="{{tabs}}" as="tab" index-as="item_no">
<section>
<child id="element-{{tab.index}}"></child>
</section>
</template>
Only <parent> knows how many instances of <child> there needs to be (<tabs> merely receives an array from <parent> and iterates over it.
Is there a way to not hard-code <child> inside the local DOM of <tabs>? Thinking of using <content> and light DOM but no idea where to even start. Would it be a promising route to take?
Desired state:
<!-- inside parent.html: -->
<tabs>
// somehow specify base content to be rendered in each tab
</tabs>
<!-- inside tabs.html: -->
<template is="dom-repeat" items="{{tabs}}" as="tab" index-as="item_no">
<section>
// somehow inject stuff from parent element, perhaps via <content>?
</section>
</template>
This is my interpretation of your question, so I am not really sure if it will be OK with you. If I misunderstood you, please drop a comment and I will gladly update my answer.
I have come up with a simple element composition, requiring no dom-repeat or manual template stamping. The solution consists of two custom elements, namely my-parent and my-child.
The definitions for both custom elements are the following:
<dom-module id="my-parent">
<template>
<tabs>
<content></content>
</tabs>
</template>
<script>
Polymer({
is: 'my-parent',
});
</script>
</dom-module>
<dom-module id="my-child">
<template>
<section>
<content></content>
</section>
</template>
<script>
Polymer({
is: 'my-child',
});
</script>
</dom-module>
And the proposed usage of them is the following:
<my-parent>
<my-child>First tab</my-child>
<my-child>Second tab</my-child>
<my-child>Third tab</my-child>
</my-parent>
Online demo: http://jsbin.com/hibuzafapu/1/edit?html,output
The resulting computed HTML code looks something like this:
<my-parent>
<tabs>
<my-child>
<section>
First tab
</section>
</my-child>
<my-child>
<section>
Second tab
</section>
</my-child>
<my-child>
<section>
Third tab
</section>
</my-child>
</tabs>
</my-parent>
If I understood you correctly, then only the <my-child> tag wrapping the <section> tag is redundant. Currently the aforementioned tag does nothing and is just a block-level element that wraps everything (just like a div). If this bothers you, then you can actually omit the <section> tag and put all the styling directly on the <my-child> tag.
In this case, the resulting computed HTML would look something like this:
<my-parent>
<tabs>
<my-child>
First tab
</my-child>
<my-child>
Second tab
</my-child>
<my-child>
Third tab
</my-child>
</tabs>
</my-parent>
UPDATE
In order to add some dynamics to the solution (adding/removing tabs), you have two options: use dom-repeat and stamp the items in light DOM, or push the items array into the my-parent element and use dom-repeat there. Both options are very similar to implement and don't have much difference in the way they work.
Option A: stamping in light DOM:
Definitions for both custom elements remain unchanged, the only difference is how you use them. Instead of hardcoding the light DOM, you make it more dynamic.
<dom-module is="tmp-element">
<template>
<my-parent>
<template is="dom-repeat" items="[[myItems]]">
<my-child>[[item.content]]</my-child>
</template>
</my-parent>
</template>
<script>
Polymer({
is: 'tmp-element',
ready: function() {
this.myItems = [
{ content: "First tab" },
{ content: "Second tab" },
{ content: "Third tab" },
],
};
});
</script>
</dom-module>
<tmp-element></tmp-element>
The tmp-element is used purely to create a binding scope and to feed the data into the dom-repeat.
Live demo: http://jsbin.com/gafisuwege/1/edit?html,console,outputenter link description here
Option B: stamping inside parent:
In this option, the parent needs to have an additional property, in which will we will supply the array of items.
The new version of the my-parent element is the following:
<dom-module id="my-parent">
<template>
<tabs>
<template is="dom-repeat" items="[[items]]">
<my-child>[[item.content]]</my-child>
</template>
</tabs>
</template>
<script>
Polymer({
is: 'my-parent',
properties: {
items: Array,
},
});
</script>
</dom-module>
And the usage is:
<dom-module is="tmp-element">
<template>
<my-parent items="[[myItems]]"></my-parent>
</template>
<script>
Polymer({
is: 'tmp-element',
ready: function() {
this.myItems = [
{ content: "First tab" },
{ content: "Second tab" },
{ content: "Third tab" },
];
},
});
</script>
</dom-module>
<tmp-element></tmp-element>
Here, I have also used a tmp-element (a different one than before) to feed the my-parent its' data.
Live demo: http://jsbin.com/kiwidaqeki/1/edit?html,console,output

Polymer 1.0 dom-repeat does not trigger filter

Have a simple paper-card with an iron-ajax which is being iterated ok but the filter I have made never triggers. The JSON being fetched via the iron-ajax has an integer value for the day of the week and I only want to have the ones with value of 0.
Tried the filter field with following values:
filter="{{isMonday}}"
filter="{{isMonday(item)}}"
filter="isMonday"
filter="isMonday(item)"
All of these with and without the observe
Component code:
<dom-module id="se-ligor">
<template>
<template is="dom-bind">
<iron-ajax auto
url="http://localhost:5000/leagues/1"
handle-as="json"
last-response="{{ajaxResponse}}">
</iron-ajax>
<template name="my-paper" is="dom-repeat" items="[[ajaxResponse]]" filter="{{isMonday}}" observe="dayofweek">
<paper-card heading="[[item.name]]">
<div class="card-content">
[[item.description]]
[[item.dayofweek]]
</div>
<div class="card-actions">
<paper-button>Some action</paper-button>
</div>
</paper-card>
</template>
</template>
</template>
<script>
Polymer({
is: "se-ligor",
isMonday: function (item) {
console.log(item.dayofweek);
if (item.dayofweek == 0)
return True;
}
});
</script>
</dom-module>
The dom-bind template is intended for binding only in index.html, not in dom-module, so that template should be removed.
The filter property takes the name of a method without delimiters (i.e., no brackets) on your Polymer constructor object.
<!-- in <dom-module> -->
<template is="dom-repeat" items="[[x]]" filter="isMonday" observe="dayofweek">...</template>
<script>
Polymer({
isMonday: function(item) {...}
});
</script>
isMonday contains a typo in return True. In JavaScript, the keyword is lowercase: true.
plunker demo

Polymer: How do you create repeating elements dynamic?

I have a Polymer element called edit-box that acts as a container for a set of unspecified elements based on incoming data.
<link rel="import" href="/assets/bower_components/polymer/polymer.html">
<link rel="import" href="/assets/elements/field-text.html">
<link rel="import" href="/assets/elements/field-hidden.html">
<polymer-element name="edit-box" attributes="dataFields">
<template>
<template repeat="{{dataField in dataFields}}">
<field-{{dataField.Type}}></field-{{dataField.Type}}>
</template>
<input type="button" value="Save" />
</template>
<script>
Polymer('edit-box', {
...
}
</script>
</polymer-element>
dataField.Type might be "text" or "hidden", etc.
I have created other polymer elements called field-text and field-hidden, etc.
This is just the basic idea, I know it's not polished. How can I go about looping through my dataFields and render different elements within this edit-box container element based on what exists inside of that data set?
It would be super cool to bind to an element name like this, but it's not possible. Polymer's internal Node.bind() library needs a property, TextNode, etc. to bind to. In the case of <field-{{dataField.type}}>, the {{}} is meaningless, as there's nothing to latch on to.
One way you can achieve what you want is to use conditional templates:
<template repeat="{{dataField in dataFields}}">
<template if="{{dataField.type == 'text'}}">
<field-text></field-text>
</template>
<template if="{{dataField.type == 'chart'}}">
<field-chart></field-chart>
</template>
</template>
http://jsbin.com/yuqinoxa/1/edit
Even better would be to create a generic element, and give it a type attribute to bind to:
<template repeat="{{dataField in dataFields}}">
<field-element type="{{dataField.type}}"></field-element>
</template>
http://jsbin.com/tirokuso/1/edit