Is it possible to find a Vue component based on its "el"? - vue.js

Suppose I have this component:
<b-select id="selectfield" :src="['a', 'b']"></b-select>
Which renders into:
<select id="selectfield">
<option value="a">a</option>
<option value="b">b</option>
</select>
Can I "find" the instance for my b-select component based on its "el" (which in this case would be its id)?

I don't know if it's possible with the "public" API but you can get the Vue instance from the __vue__ DOM property of any element that is a root mount for a component.
document.getElementById('selectfield').__vue__
As to whether or not you should use it.. Vue's author says the Vue devtools rely on it, so it's unlikely to change. See https://github.com/vuejs/vue/issues/5621

Related

how to use template in vue3 instead of native template?

I'm tring to make a SelectBox component
<Options>
<Option
v-for="person in people"
:value="person"
>
{{ person.name }}
</Option>
</Options>
And I want Option to be a dynamic component:
<component :is="props.as || 'li'">
<slot />
</component>
So I can custom content in Option (not only with <li> tag), for example use div or template to render
<Options>
<Option
v-for="person in people"
:value="person"
as="template"
>
<li>
<span>{{ person.name }}</span>
<span>other</span>
</li>
</Option>
</Options>
But if prop.as is 'template', it's rendered in html native template and can't see it in browser
I expect the result:
Vue document show some detail
But it can't work for me to add any director on it, like this
<component
:is="props.as || 'li'"
v-if="true"
>
Anyone can help me?
You probably shouldn't be using the component tag for what you're trying to do.
According to the Vue docs, here are the options of what you can pass for the is prop:
The actual component to render is determined by the is prop.
When is is a string, it could be either an HTML tag name or a component's registered name.
Alternatively, is can also be directly bound to the definition of a component.
You are trying to pass a template instead of a Vue component, which component is not designed to work with. It is unclear exactly what you are trying to do, but you can probably use a slot to accomplish it. Otherwise, to get the component tag working, define a Vue component and pass that in.

Injecting Template text as html element in vue js

I am looking for a solution where I have a line something this
This line have {{input}} in it and also the {{select}} in it.
Now this line is coming from database from backend and my frontend is Vue JS 2.6
What I want to achieve is I want to parse the {{input}} and convert it into html input tag like
<input type="text" class="some-class">
and same for the {{select}}
<select> <option value="Option value">Option Content</option> </select>
I am not sure how I inject it in the vue I am using vue with vuex
Please guide my, Any help is appreciated
I don't know if I got that right, but you now have a template string that you need to use in your component. You can try to turn the template string into a component and then use it in the parent component.
Vue.component('my-input', {
template: '<input type="text" class="some-class">'
})
<div id="parent-demo">
<my-input></my-input>
</div>
If you only have a {{input}} string and need to convert it to a component, you can write your own conversion function to convert {{input}} to a template string(<input type="text" class="some-class">), and then create the component in the same way as above

Validate vue-select with vee-validate

I'm new to VueJS.
I'm trying to validate vue-select using vee-validate.
I've tried to validate it manually but of course its not a good approach.
So, I tried to use vuelidate but couldn't get the desired result.
Now i'm trying to use vee-validate. Validation works fine as desired
but the issue is v-model.
I created a global variable product, to calculate the length of array, and passed it in v-model. So that when Its empty product's value will be zero and i can return desired result from vee-validation.
Here's the .vue html part.
<ValidationObserver>
<form #submit.prevent="add">
<div class="row row-xs mx-0">
<label class="col-sm-4 form-control-label">
<span class="tx-danger">*</span> Add product(s):
</label>
<div class="col-sm-8 mg-t-10 mg-sm-t-0">
<ValidationProvider rules="required" v-slot="{ errors }">
<v-select
name="product"
placeholder="Add product(s)"
:options="availableProducts" <-- here is the options array
:reduce="name => name"
label="name"
#input="setSelected"
v-model="product" <-- this calculates length and pass it to vee **extends**
>
</v-select>
<div v-for="error in errors" :key="error"> {{ error }} </div>
</ValidationProvider>
</div>
<!-- col-8 -->
</div>
</form>
</ValidationObserver>
Here's validation.js file
import { extend } from 'vee-validate';
extend('required', value => {
console.log(value);
return value > 0;
});
I don't want this product value there. I know its not a good approach as well. I can't pass whole array to v-model because then I can't push options in it. I can't pass a single option to v-model as well then I won't get desired result.
All I want to validate v-select when options array is empty. Any suggestions?
Veevalidate doesn't validate directly on select elements. This is my workaround.
You should create a v-field "hidden" input and a visible select v-model element. The veevalidate will take place on the v-field.
Here is an example.
<v-field type="text" class="form-control disabled" name="expirationMonth" v-model="expirationMonth" :rules="isRequired" style="display:none;"></v-field>
<select v-model="expirationMonthUI" class="form-control" #click="synchExpirationMonthUI">
<option value="January">January</option>
<option value="February">February</option>
<option value="March">March</option>
<option value="April">April</option>
<option value="May">May</option>
<option value="June">June</option>
<option value="July">July</option>
<option value="August">August</option>
<option value="September">September</option>
<option value="October">October</option>
<option value="November">November</option>
<option value="December">December</option>
</select>
<error-message name="expirationMonth"></error-message>
Then add this to your methods to synch both together.
synchExpirationMonthUI() {
this.expirationMonth = this.expirationMonthUI;
}
I have found a way of doing this, with the Rendering Complex Fields with Scoped Slots from the Vee-Validate documentation. And using the bindings from Vue Select, it looks something like this:
<Field name="supportType" v-slot="{ field }" v-model="supportType">
<v-select :options="mediaTypes" label="name" :reduce="mediaType => mediaType.id" v-bind="field">
</v-select>
</Field>
As you can see, I am using here the name, v-slot and v-model for the Field from Vee-Validate, as normal. But the v-slot is very important as it carries the information from Vue Select to Vee-Validate, or at least I think so.
On the other hand I use the options, label, reduce and v-bind from Vue Select, these I use to handle the information. So with the :options I select my dataset, with label I tell Vue Select which label to select and show from the dataset, with :reduce I tell Vue Select what will be the value of the select tag and finally use v-bind to bind the value of the select to the Vee-Validate field. So the information used on the :reduce property will be displayed on the v-model="supportType".
I tested it with a button and it worked. And I liked this way so it is not that messy and I can use the validation and other things as usual.
Hope this helps anyone.
PD: I am using Vue 3, and the latest package of both Vee-Validate and Vue Select, as of today.

How can I make the placeholder of a button dynamic in Vue?

At this moment, I have a component which is a component for a Selectbox which is used throughout the application. I want to set the placeholder dynamically, so that you can use the button wherever you want, and I know you can do this with Vue Slots, but I don't know how:
For example, I want to achieve this:
<SDSelectBox>Amount of Items</SDSelectBox>
and another use case
<SDSelectBox>How many items do you want?</SDSelectBox>
which basically replaces the placeholder of the item.
Thanks in advance.
You can make the placeholder dynamic by passing a prop to your component and then setting the placeholder to the value of the prop.
Like so:
<SDSelectBox :placeHolder="someValueFromCurrentComponent"></SDSelectBox>
//Inside the SDSelectBox
<template>
<select>
<option value="" disabled selected>{{placeholder}}</option>
...
props: ['placeHolder'],
Or with a slot you can simply insert into the SDSelectBox:
<select>
<slot></slot>
...
//parent component
<SDSelectBox>
<option value="" disabled selected>{{placeholder}}</option>
</SDSelectBox>

Vuex nested loop, how to handle v-model on select/option

In my application I need to use a nested v-for to display a list of elements with a select-option.. This is the scenario
<div class="stuck" v-for="box in items">
<p>Pick an option for this box:</p>
<select v-model="box">
<option v-for="package in packages"
:value="package.id">{{ package.name }} </option>
</select>
</div>
The variable items come from Vuex store. In this way, i'm getting the error:
You are binding v-model directly to a v-for iteration alias. This will
not be able to modify the v-for source array because writing to the
alias is like modifying a function local variable. Consider using an
array of objects and use v-model on an object property instead.
With this in mind, i'm going to change the code like so:
<div class="stuck" v-for="box in items">
<p>Pick an option for this box:</p>
<select v-model="box.id">
<option v-for="package in packages"
:value="package.id">{{ package.name }} </option>
</select>
</div>
I've just changed the select v-model from the alias box, to the right id: box.id
In this way, all works... or... half works. Because, if i'm going to pick an option from the select, i got another error:
[vuex] Do not mutate vuex store state outside mutation handlers.
This is correct, because the v-model is bind to box.id (that is not an alias but a real value). But, when i pick an option the v-model "try" to change box.id that come from Vuex store.
Now, in a simple scenario i will create a computed property for set/get to avoid vuex store mutation.
But... here i have a nested loop, so i cant create a computed on 'box.id'.
Do you have a solution for this ?
Thanks a lot!
you could try a different data flow pattern.
Your select listens to the store (but does not directly update it)
<div class="stuck" v-for="box in items">
<p>Pick an option for this box:</p>
<select :value="box.id" #change="updateBox">
<option v-for="package in packages" :value="package.id">
{{ package.name }}
</option>
</select>
</div>
Then you create a method that fires whenever the selected option changes
updateBox(e) {
const id = e.target.value;
this.$store.commit('changeYourBox', id);
},
This function should commit a vuex mutation that alteres the box id. So you'd need that mutation too.
Once the store value updates, your components box object updates and the select that listens to that store will update it's selected value accordingly.
That way, you can alter the store value from anywhere and the selected value will change as well.
with usage of mine library vuex-dot in this situation you can do so:
let's go with such state
{
state: {
boxes: []
},
mutations: {
editBox(state, {target, key, value}) {
Vue.set(target, key, value);
}
}
};
So let's create additional component BoxEdit:
<template>
<div class="stuck">
<p>Pick an option for this box:</p>
<select v-model="id">
<option v-for="package in packages"
:value="package.id">{{ package.name }} </option>
</select>
</div>
</template>
<script>
import { take } from 'vuex-dot'
export default {
props: ['box', 'packages'],
computed: {
...take('box')
.expose(['id'])
.commit('editBox', true)
.map()
}
}
</script>
and now you can make simply write
<box-edit v-for="box in boxes" :box="box" :packages="packages"></box-edit>
in your subject component template.
link to library site: https://github.com/yarsky-tgz/vuex-dot