Aurelia component slotting in markup for components model - aurelia

I am building an autocomplete component. The plan is that I can slot in some markup for what I know the component is going to bind to.
The idea is this could be any object rather than a simple display value and identifier.
I have this working using templates but I am wondering if there is a better approach.
So far it looks like this (options is hard coded for now within the components model):
// Usage:
<autocomplete>
<template replace-part="item">
//this is the content for each option within the component
<b>${option.lastName}<b/>, ${option.firstName}
</template>
</autocomplete>
//autocomplete
<template>
<input type="text" placeholder="Type 3 characters ...">
<ul>
<li repeat.for="option of options">
<template replaceable part="item"></template>
</li>
</ul>
</template>
I don't really like the templating boilerplate, slots are much nicer, is there any way to make slots work like this?
<autocomplete>
<li repeat.for="option of options">
${option.lastName}<b/>, ${option.firstName}
<li/>
</autocomplete>
//autocomplete
<template>
<input type="text" placeholder="Type 3 characters ...">
<ul>
<slot></slot>
</ul>
</template>

Slot in Aurelia is the emulation based on standard spec, which mean it doesn't work with repeat situation. repaceable was introduced to handle this scenario and I don't think we have any other options. Sometimes it feels weird but with a little documentation, probably you and your team will be fine. What you can do is for each replacement, what properties it can look for to get the item.

Related

When to split your code into components in Nuxt?

I'm coding a project in Vue, Nuxt and vuetify as UI library.
In my index page I have to show a lot of static contents and images with slides and carousels. I have coded it and everything works fine but my index.vue file in pages directory looks messy and I feel there is too many lines of code in there.
For instance I have the following code for showing a carousel in index.vue:
<v-carousel
height="90vh"
vertical
vertical-delimiters="right"
interval="9000"
cycle
:show-arrows="false"
hide-delimiter-background
>
<v-carousel-item
v-for="(item,i) in carouselItems" :key="i"
:src="item.src"
reverse-transition="scroll-y-transition"
transition="scroll-y-transition"
>
<div
class="d-flex transition-fade-in white--text"
style="height: 100%;background: linear-gradient(rgba(0,0,0,.1), rgba(0,0,0,.9));"
>
<v-row
class="fill-height pb-9 mx-5 flex-column"
align="start"
justify="end"
>
<div class="text-h3 white--text d-block mb-2">
{{ item.title }}
</div>
<div class="text-h6 white--text mx-1 text-justify"> {{ item.description }} </div>
</v-row>
</div>
</v-carousel-item>
</v-carousel>
Now my question is should I separate this code into a component and pass the options and items as props to that component or should I leave it as it is and it's OK to have a page component with many lines of code?
It all depends on you, the way you work and how you/your team feel like about splitting into components. There is no standard as of to say something like "a component needs to be at max 300 lines" or something alike.
It also comes down to logic with your components, make them do 1 thing well rather than too much at the same time. But don't over-engineer it by having 50 lines of components that could totally be a single component neither because it will also introduce some complexity that is not needed.
Just code and split when you feel like it's doing too much or it's to big. I usually have components from 5 lines of code up to 300~400 as a maximum depending of their complexity/implementation.

VueJS 2 - Re-useable parent component that can accept different children

I have a parent component, SearchComponent:
<template>
<div>
<div class="relative pl-8 pr-10 rounded bg-white border focus-within:bg-white focus-within:ring-1">
<input v-focus #keyup.escape="clearSearch" #keyup="doSearch" v-model="searchTerm"
class="w-full ml-4 h-12 pl-1 text-gray-700 text-lg rounded-full border-0 bg-transparent focus:outline-none placeholder-gray-400"
placeholder="Search..." autocomplete="off">
</div>
<ul class="bg-white mt-4">
<quick-search-item v-for="item in searchResults" :key="item.id" :item-data="item.itemData">
</quick-search-item>
</ul>
</div>
</template>
This is responsible for receiving user input and getting results from an ajax call, handling errors etc. and generating the result list.
What I'd like to do is to make this generic so that instead of having a quick-search-item child component I can pass in different types of child component (like car-search-item, person-search-item etc.) depending on the context of where the user is in the app and what they're searching for
I've read a number of tutorials and I couldn't find quite what I'm trying to do. This may mean I'm approaching this in the wrong way - but if anyone could point me in the right direction, or has a better approach, I'd be very grateful.
Thanks,
Lenny.
I would try to make use of the <slot> element. Check out the documentation here
<parent-component>
<slot></slot>
</parent-component>
Hope this can put you on the right path.
Schalk Pretorius was quite right: slots are the answer to this, specifically scoped slots. I found the Vue docs a little confusing as it refers to getting data from the child component and I wanted to do it the other way around, so as an aide memoire for myself and to help anyone else here's what I did:
In my parent component I defined the slot like this:
<slot name="results" v-bind:items="searchResults">
</slot>
The v-bind binds searchResults (a data item in the parent component) to the value 'items'. 'items' then becomes available in the slot.
In my child component I have a simple property setup called items:
props: {
items: {type: Array},
},
Then to hook it all together in my Blade file I did this:
<search-component endpoint="{{ route('quick_search.index') }}">
<template v-slot:results="props">
<food-results :items="props.items">
</food-results>
</template>
</search-component>
This creates the search-component. Inside that -as I'm using named slots - we need a template and use v-slot to tell it which slot to use (results), then the ="props" exposes all the properties we've defined on that slot (in this case just one, 'items').
Inside the template we put our child component and then we can bind items to props.items which will be the searchResults in our parent component.
I'm happy to have this working and I can now create lots of different results components while reusing the search component - and at least I learnt something today!
Cheers,
Lenny.

How to properly create a popup component in Vue 3

As part of becoming a better Vue programmer, I am trying to implement a popup similar to Popper with a clean and Vueish architecture. Here is a simple schematic that I came up with:
So basically there is a target component, which is the reference for the popup's position. The popup can be positioned above, below, right and left of the target, therefore I will need to have access to the target element in my popup. Also, the target can be an arbitrary component. It can be a simple button or span, but also something much more complex.
Then there is the popup itself, which will be put into a modal at the end of the body, It contains the actual content. The content again can be an arbitrary component.
I have a working implementation of the popup, but the basic structure seems to be far from perfect. I am using two slots, one for the target element and one for the content.
Here is what I have come up with so far for the template:
<template>
<div ref="targetContainer">
<slot name="target"></slot>
</div>
<teleport to="body">
<div v-show="show" class="modal" ref="modal">
<div ref="popover" class="popover" :style="{top: popoverTop + 'px', left: popoverLeft + 'px'}">
<slot name="content"></slot>
</div>
</div>
</teleport>
</template>
There are several issues with this that I am not really happy with.
Using the popup is not very simple
When using this popup in another component, two <template> tags are rquired. This is ungly and not very intuitive. A very simple use case looks like this:
<modal :show="showPopup" #close="showPopup=false">
<template v-slot:target>
<button #click="showPopup=true"></button>
</template>
<template v-slot:content>
<div>Hello World!</div>
</template>
</modal>
The target is wrapped in another <div>
This is done to get access to the target element, that I need for the layout. In mounted() I am referencing the target element like this:
let targetElement = this.$refs.targetContainer.children[0];
Is this really the best way to do this? I would like to get rid of the wrapping <div> element, which just asks for unintended side effects.
The best solution would be to get rid of one slot and somehow reference the target element in another way because I only need its layout information, it does not have to be rendered inside the popover component.
Can someone point me in the right direction?
Here is my solution, which was inspired by a comment on my question and which I think is worth sharing.
Instead of putting the target element into a slot, I am now passing its ref as a prop, which makes things much cleaner.
The popover component's template now looks like this.
<template>
<teleport to="body">
<div v-show="show" class="modal" ref="modal">
<div ref="popover" class="popover" :style="{top: popoverTop + 'px', left: popoverLeft + 'px'}">
<slot ref="content"></slot>
</div>
</div>
</teleport>
</template>
I has a targetRefprop, so the component can be simply used like this:
<div ref="myTargetElement" #click="isPopupVisible=true">
</div>
<modal :show="isPopupVisible" #close="isPopupVisible=false" targetRef="myTargetElement">
<!-- popup content goes here -->
</modal>
And after mounting I can access the target element like this:
let targetElement = this.$parent.$refs[this.targetRef];
I like this solution a lot. However, ideas, advice or words of caution are still highly welcome.

Passing slots through from Parent to Child Components

I have built a user-defined component (async-select) on top of another component (vue mutliselect) like this:
https://jsfiddle.net/2x7n4rL6/4/
Since the original vue-multiselect component offers a couple of slots, I don't want to loose the chance to use them. So my goal is to make these slots available from inside my custom component. In other words, I want to something like this:
https://jsfiddle.net/2x7n4rL6/3/
But that code oes not work.
However, if I add the slot to the child component itself, it works just fine (which you can see from the fact that options become red-colored).
https://jsfiddle.net/2x7n4rL6/1/
After surfing the web, I have come across this article, but it does not seem to work
Is there any way in VueJS to accomplish this ?
Slots can be confusing!
First, you need a template element to define the slot content:
<async-select :value="value" :options="options">
<template v-slot:option-tmpl="{ props }">
<div class="ui grid">
<div style="color: red">{{ props.option.name }}</div>
</div>
</template>
</async-select>
Then, in the parent component, you need a slot element. That slot element itself can be inside of another template element, so its contents can be put in a slot of its own parent.
<multiselect
label="name"
ref="multiselect"
v-model="localValue"
placeholder="My component"
:options="options"
:multiple="false"
:taggable="false">
<template slot="option" slot-scope="props">
<slot name="option-tmpl" :props="props"></slot>
</template>
</multiselect>
Working Fiddle: https://jsfiddle.net/thebluenile/ph0s1jda/

Vue.js not compatible with CSS Grid tables [duplicate]

List item
<ul>
<li>language</li>
< v-if= "tree()"> //which tag I may use or any other process
<li>home</li>
<li>about</li>
<>
< v-else> //which tag I may use or any other process
<li>accounts</li>
<li>listing</li>
<>
</ul>'
In the V-if which html tag i may use or any other vue.js process to work with this.
You can use template:
<template v-if="condition">
</template>
<template v-else>
</template>
Template will not be rendered in the browser. But it will parse the contents inside of this to the html.
you can sometimes use the <slot> element to make what you want. Have a look at the slot documentation here