i have toggle and reset button:
<template>
<label :for='id + "_button"' :class='{"active": isActive}' class='toggle__button'>
<input type='checkbox' :id='id + "_button"' v-model='checkedValue'>
<span class='toggle__switch'></span>
<button #click='reset()'>reset</button>
</label>
</template>
script:
export default ({
props: {
defaultState: {
type: Boolean,
default: false
},
id: {
type: String,
default: 'primary'
}
},
data() {
return {
currentState: this.defaultState
};
},
computed: {
isActive() {
return this.currentState;
},
checkedValue: {
get() {
return this.defaultState;
},
set(newValue) {
this.currentState = newValue;
this.$emit('change', newValue);
}
}
},
methods: {
reset() {
this.checkedValue = false ;
}
}
});
toggle works well. But when you press the reset button, a bug appears. At which, toggle works only from the 2nd time. What needs to be added/fixed?
Your component is a custom component which could interact with parent one using two-way binding by defining a value as prop and emit the new value when the checkbox changes state :
<template>
<label :for='id + "_button"' :class='{"active": isActive}' class='toggle__button'>
<input type='checkbox' :id='id + "_button"' :value='value' #change="emitVal($event)">
<span class='toggle__switch'></span>
<button #click='emitVal(false)'>reset</button>
</label>
</template>
script :
export default ({
props: {
value: {
type: Boolean,
default: false
},
id: {
type: String,
default: 'primary'
}
},
methods: {
emitVal(val) {
this.$emit('input',val)
}
}
});
in parent :
<customCheckbox id="secondary" v-model="someValue"/>
Related
I am trying to check if the Multi-select component is empty. But upon checking it kept telling me it's not null. Do I need to use a v-model for this?
Here's the code for the Multi-select component:
<template>
<div>
<Multiselect
v-model="value"
mode="tags"
:close-on-select="false"
:searchable="false"
:create-option="true"
:options="multi_options"
class="multiselect-orange multiselect"
/>
</div>
</template>
<script>
import Multiselect from "#vueform/multiselect";
export default {
components: {
Multiselect,
},
props: {
multi_options: {
type: Array,
required: true,
},
inputSelected:{
required:true,
},
method: { type: Function },
// default: {
// type: String,
// required: false,
// default: null,
// },
},
data() {
return {
value: this.inputSelected,
};
},
mounted() {
this.$emit("set-input", this.value);
},
watch: {
value: function () {
this.$emit("set-input", this.value);
},
},
};
</script>
Here's the code where I used the component:
<div class="inputHolder">
<label class="body"> Skills:</label>
<MultiSelect :multi_options="this.skills" #set-input="setSkillSet" :inputSelected="staffSkills"/>
</div>
Here's the code when I tried to check if the Multi-select is empty:
checkForm(){
this.errors = []
if (this.staffSkills) {
return true;
}
if(this.staffSkills === []){
this.errors.push('Select at least one.');
}
if(this.errors.length){
return this.errors
}
}
},
I created a select2 wrapper in vue3 with options API everything working fine but the problem is that when getting values from calling API it's not selected the default value in the select2 option. but when I created a static array of objects it does. I don't know why it's working when it comes from the API
Parent Component
Here you can I passed the static options array in options props and my selected value is 2 and it's selected in my Select2 component, but when passed formattedCompanies it's not which is the same format as the static options array then why is not selected any reason here..?
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
:options="options"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : [],
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
Select2 Component
Here is what my select2 component look like, did I do anything wrong here, please anybody help me
<template>
<select class="form-control">
<slot/>
</select>
</template>
<script>
export default {
name: "Select2",
props: {
options: {
type: [Array, Object],
required: true
},
modelValue: [String, Number],
placeholder: {
type: String,
default: "Search"
},
allowClear: {
type: Boolean,
default: true
},
},
mounted() {
const vm = this;
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
},
watch: {
modelValue(value) { // update value
$(this.$el)
.val(value)
.trigger("change");
},
options(options) { // update options
$(this.$el)
.empty()
.select2({data: options});
},
},
destroyed() {
$(this.$el)
.off()
.select2("destroy");
}
}
</script>
Probably when this Select2 mounted there is no companies. It is empty array after that it will make API call and it it populates options field and clear all options.
Make:
companies : null,
Change it to
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
It should be like this:
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : null,
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
The problem was that my parent component and Select2 component mounted at the same time that's why my computed value is not initialized so the selected value is not selected in the option,
problem solved by setTimeOut function in mounted like this
Select2 Component
<script>
mounted() {
const vm = this;
setTimeout(() => {
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
}, 500)
},
</script>
I have a custom input where I recieve a value prop and emit a input event on the input event. I can use this custom input without problems with a model, now I'm creating a custom password input that I initialize as a custom input but I can't bind the model using value and input event handlers (passing them to the custom input). How can I approach this?
Custom Input:
My program model > custom input (value and input event handler) : works
My program model > custom password (value and input event handler) > custom input: doesn't work.
Code:
Input.vue:
<template>
<div class="form-group">
<label for="" v-if="typeof label !== 'undefined'">{{ label }}</label>
<!-- GROUP -->
<template v-if="isGroup">
<div class="input-group">
<!-- PREPEND -->
<div v-if="hasPrepend" class="input-group-prepend"
:class="{'inside bg-transparent' : prependInside, 'pointer': prependPointer}"
#click="clickPrepend">
<span class="input-group-text"
:class="{'bg-transparent' : prependInside}">
<i aria-hidden="true"
v-if="prependType === 'icon'"
:class="'fa fa-' + prependContent"></i>
<template v-if="prependType === 'text'">{{ prependContent }}</template>
</span>
</div>
<!-- INPUT -->
<input class="form-control"
:type="type"
:class="generatedInputClass"
:readonly="readonly"
:disabled="disabled"
:value="value"
#input="inputEvent"
#change="onChange">
<!-- APPEND -->
<div v-if="hasAppend" class="input-group-append"
:class="{'inside bg-transparent' : appendInside, 'pointer': appendPointer}"
#click="clickAppend">
<span class="input-group-text"
:class="{'bg-transparent' : appendInside}">
<i aria-hidden="true"
v-if="appendType === 'icon'"
:class="'fa fa-' + appendContent"></i>
<template v-if="appendType === 'text'">{{ appendContent }}</template>
</span>
</div>
</div>
</template>
<!-- INPUT -->
<template v-else>
<input class="form-control"
:type="type"
:class="generatedInputClass"
:readonly="readonly"
:disabled="disabled"
:value="value"
#input="inputEvent"
#change="onChange"
>
</template>
<small class="form-text"
v-if="typeof helpText !== 'undefined'"
:class="generatedHelperClass">
{{ helpText }}
</small>
</div>
</template>
<script>
export default {
name: 'InputGroup',
props: {
value: String,
label: String,
helpText: String,
size: String,
prependContent: String,
appendContent: String,
prependType: {
type: String,
default: 'icon',
},
appendType: {
type: String,
default: 'icon',
},
prependInside: {
type: Boolean,
default: false,
},
appendInside: {
type: Boolean,
default: false,
},
prependPointer: {
type: Boolean,
default: false,
},
appendPointer: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
type: {
type: String,
default: 'text',
},
valid: {
type: Boolean,
default: null,
},
},
watch: {
valid() {
},
},
computed: {
isGroup() {
return this.hasPrepend || this.hasAppend;
},
hasPrepend() {
return typeof this.prependContent !== 'undefined';
},
hasAppend() {
return typeof this.appendContent !== 'undefined';
},
generatedInputClass() {
const size = typeof this.size !== 'undefined' ? `form-control-${this.size}` : '';
let valid = '';
if (this.valid !== null) {
valid = this.valid ? 'is-valid' : 'is-invalid';
}
return `${size} ${valid}`;
},
generatedHelperClass() {
let valid = 'text-muted';
if (this.valid !== null) {
valid = this.valid ? 'valid-feedback' : 'invalid-feedback';
}
return `${valid}`;
},
},
methods: {
inputEvent(e) {
this.$emit('input', e.target.value);
},
clickPrepend(e) {
this.$emit('click-prepend', e);
},
clickAppend(e) {
this.$emit('click-append', e);
},
onChange(e) {
this.$emit('change', this.value, e);
},
},
};
</script>
Password.vue:
<template>
<div>
<app-input
:label="label"
:type="type"
prepend-content="lock"
:append-content="passwordIcon"
:append-inside="true"
:append-pointer="true"
#click-append="tooglePassword"
:value="value"
#input="inputEvent">
</app-input>
</div>
</template>
<script>
import Input from './Input';
export default {
name: 'Password',
components: {
appInput: Input,
},
props: {
value: String,
label: {
type: String,
default: 'ContraseƱa',
},
readonly: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
valid: {
type: Boolean,
default: null,
},
},
data() {
return {
pass: '',
type: 'password',
};
},
computed: {
passwordIcon() {
return this.type === 'password' ? 'eye' : 'eye-slash';
},
},
methods: {
tooglePassword() {
this.type = this.type === 'password' ? 'text' : 'password';
},
inputEvent(e) {
this.$emit('input', e.target.value);
},
},
};
</script>
The thing is the input event your Password listens form app-input component, has as value the actual string value already, not the element (that you'd have to call e.target.value to get the string value)
In other words, in Password.vue, instead of:
inputEvent(e) {
this.$emit('input', e.target.value);
},
Do:
inputEvent(e) {
this.$emit('input', e);
},
CodeSandbox demo here.
As a vuejs component, I want to be able to display a character counter next to my input field.
The field is initially set up using a prop (this.initialValue).
When the method this.updateCounter is called the input textfield is blocked : typing into the field won't update its value. If I don't set the maxlength prop, the field is working fine : I can update the textfield.
Usage in a template :
<textfield maxlength="50" name="title" initialValue="Test"></textfield>
Here is the component code :
<template>
<div class="input">
<div class="input__field">
<span class="input__limit f--small">{{ counter }}</span>
<input type="text" :name="name" :maxlength="computedMaxlength" v-model="currentValue" />
</div>
</div>
</template>
<script>
export default {
name: 'Textfield',
props: {
name: {
default: ''
},
maxlength: {
default: 0
},
initialValue: {
default: ''
}
},
computed: {
hasMaxlength: function () {
return this.maxlength > 0;
},
computedMaxlength: function () {
if(this.hasMaxlength) return this.maxlength;
else return false;
},
currentValue: {
get: function() {
return this.initialValue;
},
set: function(newValue) {
this.updateCounter(newValue);
this.$emit("change", newValue);
}
}
},
data: function () {
return {
counter: 0
}
},
methods: {
updateCounter: function (newValue) {
if(this.maxlength > 0) this.counter = this.maxlength - newValue.length;
}
},
mounted: function() {
this.updateCounter(this.initialValue);
}
}
</script>
Edit
I have fixed my issue by not using v-model but instead using a value and an input event.
data: function () {
return {
value: this.initialValue,
counter: 0
}
},
methods: {
updateCounter: function (newValue) {
if(this.maxlength > 0) this.counter = this.maxlength - newValue.toString().length;
},
onInput: function(event) {
const newValue = event.target.value;
this.value = newValue;
this.updateCounter(newValue);
this.$emit("change", newValue);
}
},
I'm trying to figure out which approach is more appropriate / less resource intense. Both examples below will do the same job, but my understanding is that there might be situations where both events #keyup and #change might be triggered unnecessarily - hence my question, whether watch method would be a better option here?
The reason why I also need to check for a #change event is when someone simply selects saved input value rather than type it.
<template>
<input
type="text"
:name="name"
#keyup="update()"
#change="update()"
v-model="field"
>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
initialValue: {
type: String,
required: false,
default: ''
}
},
data() {
return {
field: this.initialValue
}
},
mounted() {
this.update();
},
methods: {
update() {
this.$emit('input', this.field);
}
}
}
</script>
vs
<template>
<input
type="text"
:name="name"
v-model="field"
>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
initialValue: {
type: String,
required: false,
default: ''
}
},
data() {
return {
field: this.initialValue
}
},
mounted() {
this.update();
},
watch: {
field() {
this.update();
}
},
methods: {
update() {
this.$emit('input', this.field);
}
}
}
</script>