Custom Input Vue 3 Composition API - vue.js

This is BaseInput.vue's template, as you know in Vue3 we have $attrs (fallthrough props) that we can specify where to bind prop value as below I bind it to element with the intention of customize css style for input element.
BaseInput.vue
<template>
<label :class="$style.wrapper">
<input
v-bind="$attrs"
:value="modelValue"
/>
<span :class="$style.label">
<slot name="label"></slot>
</span>
</label>
</template>
Then I call
<BaseInput
v-model="username"
:class="$style.field"
/>
<BaseInput
v-model="password"
:class="$style.field"
/ >
So basically the class field will be applied to <input/ > element in BaseInput.vue component thanks to v-bind='$attrs'.
But sometimes, I want to add css style for the wrapper element of BaseInput.vue (here is element).
For example:
margin-bottom: 32px;
I want the first has margin bottom = 32px, but the second one doesn't.
So what is the good way to achive that?
I just found a solution that we add a div wrapper for each but I think it's not really good idea. So please elaborate me.

Related

vue passing props to sub component will generate html attributes to wrapper root

So I have this component PageWrapper that has a title props.
<div id="page-wrapper">
<h1>{{title}}</h1>
<slot />
</div>
And I have a wrapper component that forwards most props to its child:
<PageWrapper v-bind="[$attrs]">
<slot name="default"/>
</PageWrapper>
It works as expected, except that my page wrapper root has an extra title html attribute that I don't want to be there.
<div id="page-wrapper" title="My Title">
<h1>My title</h1>
<!-- rest of the page -->
</div>
I have the feeling there is a confusion between html attributes and vue properties when forwarding to sub-component.
I thought this had to do with the difference between $props and $attrs but apparently not as it doesn't work in any case.
Can someone clarify this for me? How can I avoid this html-attributes behavior?
Cheers
you do not want to bind the attributes to the PageWrapper Component.
Instead just bind the title property.
<PageWrapper :title="title">
<slot name="default"/>
</PageWrapper>
EDIT:
Might also try to bind the attributes without the square brackets v-bind="$attrs"

Display translation on a component in Vuejs

I want to show a translation on the title of a component.
Here is the HTML code:
<user-card
:totalUser="totalUsers"
color="primary"
icon="UserIcon"
user-title="Total users"
/>
On my user-card component I have this:
<b-card class="text-center">
<b-avatar
:variant="`light-${color}`"
class="mb-1"
size="45"
>
<feather-icon
:icon="icon"
size="21"
/>
</b-avatar>
<div class="truncate">
<h2 class="mb-25 font-weight-bolder">
{{ totalUser}}
</h2>
<span>{{ user-title }}</span>
</div>
</b-card>
And to use translation I have this syntax where I get the translated terms from the JSON file:
{{$t("Total users")}}
How can I implement this on the user-title?
Have a look at this, I have tried to replicate your scenario in code sandbox.
sample app
What you are doing wrong is that $t is a function that accepts variable name which has an actual message in it, first you have to define a variable e.g totalUserTitle: 'Total users' for multiple languages like I did in index.js, and then you can use it as $t(totalUserTitle).
Just use v-bind and pass the expression to your user-card component directly:
<user-card
...
:user-title="$t('Total users')"
/>
You're actually already using this syntax in multiple places, this directive just tells Vue to "dynamically bind one or more attributes, or a component prop to an expression", which is exactly what you're looking for here.
This will evaluate $t('Total users') as an expression and then pass the result to your component as a prop.

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.

Aurelia component slotting in markup for components model

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.

Passing Form Data Between Components & Vue Routing

I am trying to create a step-by-step form using Components & Routing. If there is a better or easier approach to do this, please feel free to suggest, since I am new to Vue.js.
I have a and 3 templates.
<template id="step-1">
<h1>Welcome to Form</h1>
</template>
<template id="step-2">
<label>Name:</label>
<input type="text" name="name" v-model="name" />
<br />
<label>Email:</label>
<input type="email" name="email" v-model="email" />
</template>
<template id="step-3">
<p>Review:</p>
<!-- Display Step 2 Form Values -->
{{ name }}
{{ email }}
<button>Submit</button>
</template>
What I want to do is, display the input values on #step-3, and on a button click, submit the form via an ajax call.
You can view the Fiddle from here: https://jsfiddle.net/j7mwc9wk/
One way to do this is for all three components use the same data object. For the purposes of this bit of code it can be a simple javascript object. A bit more sophisticated approach is to use Vuex an official Vue data store.
You could also have these three components have the same parent in which case name and email would be properties of parent data method and therefore accessible to all children. I don't know how this would work with Vue router but it should be fine.