capture the content of a component child, this component and this in turn calls another, in this second component there is an html, in this second component in the code javascripot capture this component to use it
first component, has as component child -- field --
<template>
<div id="login">
<field type="select" name="lang">
<option>one</option>
<option>zero</option>
</field>
</div>
</template>
second component, is field
<template>
<div class="field input">
<label>{{label}}</label>
<div v-html="htmlfield"> </div>
</div>
</template>
<script>
export default {
props: ['type'],
computed : {
htmlfield : function(){
switch (this.type) {
case 'select':
return `<select ${attr}>**__here__**</select>`; break;
}
},
},
}
</script>
__here__ I want to put the options indicated in the first component
I would like to be able to capture with javascript the html of a component
It looks like you're trying to create a universal <field> that you can pass the type of input you want.
Can I suggest a different approach?
First component
<template>
<div id="login">
<field type="select" :options="options" name="lang"></field>
</div>
</template>
<script>
export default {
data () {
return {
options: [
{ label: 'one', value: 'value1' },
{ label: 'zero', value: 'value2' }
]
}
}
}
</script>
Second component:
<template>
<div class="field input">
<label>{{ label }}</label>
<select v-if="type === 'select'">
<option v-for="option in options" :value="option.value" :key="option.value">{{ option.label }}</option>
</select>
<input v-if="type === 'text'" type="text">
<input v-if="type === 'number'" type="number">
</div>
</template>
<script>
export default {
props: ['label', 'type', 'options']
}
</script>
Here, instead of writing the <option> elements in the parent template, we create option objects in an array called options and pass it to <field> as a prop, then we can render the <option> elements in the child IF the 'type' === 'select'.
Related
I have a list of properties in my database. I want the user to select the type of houses in the property.
Property
Selected property types
Property 1
['studio_apartment', 'one_bedroom', 'two_bedroom']
Property 2
['studio_apartment', 'one_bedroom']
To achieve this I am using the Vue v-for loop as follows:
<template>
<div v-for="(name, index) in propertyNames" :key="index">
NAME: {{ name }}<br />
<input
type="checkbox"
value="studio_apartment"
v-model="form.property_type"
/>Studio apartment<br />
<input
type="checkbox"
value="one_bedroom"
v-model="form.property_type"
/>One bedroom<br />
<input
type="checkbox"
value="two_bedroom"
v-model="form.property_type"
/>Two bedroom<br />
SELECTED: {{}}
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
form: {
property_type: [],
},
};
},
</script>
The problem is when I select for example studio_apartment in property 1, then all the properties studio_apartment is checked. How do I only get the selected checkboxes for each property individually. Thanks.
This happens because there's only one form instance being shared in each loop iteration.
You could change form into an object array based on propertyNames, so that they have the same array length; then index into form with the iteration index of v-for:
<template>
<div>
<div v-for="(name, index) in propertyNames" :key="index">
NAME: {{ name }}<br>
<input type="checkbox" value="studio_apartment" v-model="form[index].property_type">Studio apartment<br>
<input type="checkbox" value="one_bedroom" v-model="form[index].property_type">One bedroom<br>
<input type="checkbox" value="two_bedroom" v-model="form[index].property_type">Two bedroom<br>
</div>
</div>
</template>
<script>
export default {
data() {
const propertyNames = ['Property 1', 'Property 2']
return {
propertyNames,
form: propertyNames.map(name => ({ property_type: [] }))
}
}
}
</script>
demo
I'm encountering a very strange case.
I've build a very simple example in order to present my problem.
I've 3 files : App.vue, todo2.vue, todoI.vue.
App.vue has 1 component (todo2.vue). todo2.vue has 1 component (todoI.vue).
You'll find under the code of todo2 and todoI.
The problem I'm facing is that when i press enter in the input text id #checkboxAdd, it triggers an event on the next button.
In the code below when pressing enter in the input text #checkboxAdd, it triggers the click event on the first iteration of my todoI button, which in my example calls the function del (#click="del()"), which console.log(this), logging the first iteration of the component todoI.
What is even stranger is that when I add a button just after the input text, add a #click to console.log the event, it is indeed called (instead of calling the button of the first iteration of todoI).
Does anyone understand why this happens ? Is this something I'm doing wrong ?
todo2.vue:
<template>
<div class="d-flex flex-column">
<div>
<form #submit.prevent="">
<div class="mb-3 input-group">
<div class="input-group-text">
<input type="checkbox" class="form-check-input" id="checkboxAdd" aria-describedby="checkboxAdd">
</div>
<input type="text" class="form-control" id="inputAdd" aria-describedby="inputAdd" v-model="tempI">
</div>
<ul class="list-group">
<todoI v-for="(it, k) in todos" v-model="it.v" :key="k" #delItem="del(it)"></todoI>
</ul>
<br>
</form>
</div>
</div>
</template>
<script>
import todoI from './todoI.vue'
export default {
name:"todo2",
components: {todoI},
data: function(){
return {
todos: [
{v:{
val: "Bread",
checked: false
}},
{v:{
val: "Fruits",
checked: false
}},
{v:{
val: "Ironing",
checked: false
}}
],
tempI: ''
}
},
methods:{
del (it){
this.todos = this.todos.filter(i => i!==it)
}
}
}
</script>
todoI.vue:
<template>
<li class="list-group-item d-flex align-items-center" #mouseover="btnShow=true" #mouseleave="btnShow=false">
<input type="checkbox" class="me-4" v-model="value.checked">
<div class="w-100" :class="value.checked ? checkedClass : '' ">{{ value.val }}</div>
<div class="flex-shrink-1">
<button class="btn btn-sm btn-close" v-show="btnShow" #click="del()"></button>
</div>
</li>
</template>
<script>
export default {
name:"todoI",
props:{
value: Object
},
data: function(){
return {
checkedClass:['text-decoration-line-through', 'text-muted'],
btnShow: false,
}
},
methods:{
del(){
console.log(this)
}
}
}
</script>
you can simple use #keypress or #keydown
<input type="text" class="form-control" id="inputAdd" v-model="tempI" #keypress.enter.prevent />
or
<input type="text" class="form-control" id="inputAdd" v-model="tempI" #keydown.enter.prevent = "">
I want to create a simple form builder with Vue where users click on buttons from a menu to add different form fields to a form. I know that if there was just one type of form field to add, I could do it with something like this (https://jsfiddle.net/u6j1uc3u/32/):
<div id="app">
<form-input v-for="field in fields"></form-input>
<button type="button" v-on:click="addFormElement()">Add Form Element</button>
</div>
<script type="x-template" id="form-input">
<div>
<label>Text</label>
<input type="text" />
</div>
</script>
And:
Vue.component('form-input', {
template: '#form-input'
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
},
methods: {
addFormElement: function() {
this.fields.push({type: 'text', placeholder: 'Textbox ' + (++this.count)});
}
}
})
But what if there's more than one type of form field (input, file, select, etc...)? I was thinking maybe build a different component for each type, but then how would I show multiple types of components in a single list of form elements?
Could I maybe create a component with children components of different types based on the data in the fields array?
Or is there a better way to go about this situation that I'm missing? I've just started learning Vue, so any help is appreciated!
Ok, so I looked into dynamic elements and managed to pull this together:
Vue.component('form-input', {
template: '#form-input'
});
Vue.component('form-select', {
template: '#form-select'
});
Vue.component('form-textarea', {
template: '#form-textarea'
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
},
methods: {
addFormElement: function(type) {
this.fields.push({
'type': type,
id: this.count++
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<component v-for="field in fields" v-bind:is="field.type" :key="field.id"></component>
<button type="button" v-on:click="addFormElement('form-input')">Add Textbox</button>
<button type="button" v-on:click="addFormElement('form-select')">Add Select</button>
<button type="button" v-on:click="addFormElement('form-textarea')">Add Textarea</button>
</div>
<script type="x-template" id="form-input">
<div>
<label>Text</label>
<input type="text" />
</div>
</script>
<script type="x-template" id="form-select">
<div>
<label>Select</label>
<select>
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</script>
<script type="x-template" id="form-textarea">
<div>
<label>Textarea</label>
<textarea></textarea>
</div>
</script>
So instead of creating a new form-input component for each item in the fields array, I'm creating a new component that is associated with the correct component via the type property of the fields
You can pass the field object as props of your form-input component and make the type dynamic:
Vue.component('form-input', {
template: '#form-input',
props: ['field']
})
new Vue({
el: '#app',
data: {
fields: [],
inputType: '',
count: 0
},
methods: {
addFormElement(val) {
this.fields.push({type: val, placeholder: 'Textbox ' + (++this.count)});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<h3>Add form element</h3>
<select size="3" v-model='inputType' #click="addFormElement(inputType)">
<option value="text">Text</option>
<option value="checkbox">Checkbox</option>
<option value="radio">Radio</option>
</select>
<p>
<form-input v-for="field in fields" :field="field"></form-input>
</p>
</div>
<template id="form-input">
<div>
<label>{{ field.type }}</label>
<input :type="field.type" />
</div>
</template>
Based on the code from the answer, one could add dynamic content for each one of those form controls as well ( the full concept could be seen from the following site):
Vue.component('form-input', {
template: '#form-input'
, props: ['label','cnt']
});
Vue.component('form-select', {
template: '#form-select'
, props: ['label','cnt']
});
Vue.component('form-textarea', {
template: '#form-textarea'
, props: ['label','cnt']
});
new Vue({
el: '#app',
data: {
fields: [],
count: 0
}
, mounted() {
// fetch those from back-end
this.addFormElement('form-input','lbl', "form-input-content")
this.addFormElement('form-textarea','lbl', "form-textarea-content")
var select_cnt = [
{'value': 1, 'text': 'item-01'},
{'value': 2, 'text': 'item-02'}
]
this.addFormElement('form-select','some-label',select_cnt)
}
, methods: {
addFormElement: function(type,label,cnt) {
this.fields.push({
'type': type
, id: this.count++
, 'label':label
, 'cnt':cnt
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<component v-for="field in fields" v-bind:is="field.type" :key="field.id" :cnt="field.cnt" :label="field.label"></component>
</div>
<script type="x-template" id="form-input">
<div v-on:keyup.tab="this.document.execCommand('selectAll',false,null);">
<label>{{label}}</label>
<input type="text" :value="cnt"/>
</div>
</script>
<script type="x-template" id="form-textarea">
<div v-on:keyup.tab="this.document.execCommand('selectAll',false,null);">
<label>{{label}}</label>
<textarea :value="cnt"></textarea>
</div>
</script>
<script type="x-template" id="form-select">
<div>
<label>Select</label>
<select>
<option v-for="oitem in cnt" :value="oitem.value">{{oitem.text}}</option>
</select>
</div>
<div v-html="cnt"></div>
</script>
i have the following vue component
<template>
<div id="input-div">
<label v-if="label">{{ label }}</label>
<select :multiple="multiple" :value="value" #change="change($event.target.value)">
<slot></slot>
</select>
<div class="error"><slot name="error"></slot></div>
</div>
</template>
<script>
export default {
name: 'ha-select',
props: ['label', 'value', 'multiple'],
methods: {
change(value) {
this.$emit('input', value);
}
}
}
</script>
and since it is a select component i want it to be usable with v-model which works when the multiple attribute is not set but when i set multiple pass v-model an Array instead of appending the selected value to the Array, the array just gets replaced by the value of the current selected option. maybe it has something to do with the way i am emitting the input event. So how do i create a custom select component with multiple support?
I've modified the code slightly to use a local variable bound to the select with v-model and emit the bound value. This results in emitting the array of selected values when multiple is true.
console.clear()
const CustomSelect = {
template: `
<div id="input-div">
<label v-if="label">{{ label }}</label>
<select :multiple="multiple" v-model="selected" #change="$emit('input', selected)">
<slot></slot>
</select>
<div class="error"><slot name="error"></slot></div>
</div>
`,
name: 'ha-select',
props: ['label', 'value', 'multiple'],
data(){
return {
selected:this.value
}
},
}
new Vue({
el:"#app",
data:{
albums: []
},
components: {
CustomSelect
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<custom-select label="Pink Floyd Albums" v-model="albums" :multiple="true">
<option value="1">Dark Side of the Moon</option>
<option value="2">Animals</option>
<option value="3">The Wall</option>
</custom-select>
<div>
Selected Albums {{albums}}
</div>
</div>
I have a Vue component that has a 'select state' dropdown element. I want to add a js function that populates the options with the 50 states instead of having to hard code them. I will also have this dropdown in a couple other places so I want this function to be accessed from outside the component. What is the best way to accomplish this?
<template>
<div class="section" v-bind:class="sectionClass" data-mh="group3">
<h3>{{sectionTitle}}</h3>
<div class="section-content display-area">
<i class="icon icon-health img-left"></i>
<form>
<div class="row mt">
<div class="col-xs-12 col-sm-8">
<div class="same-height-parent">
<div class="same-height">
<input type="text" class="form-control" placeholder="Enter Name">
</div>
<div class="same-height">
<select name="state" id="state" class="form-control" tabindex="9">
<option value="">- Select State -</option>
</select>
</div>
</div>
<!-- same-height-parent -->
</div>
<div class="col-xs-12 col-sm-4">
<button type="submit" class="btn btn-success btn-block btn-fz20">Search</button>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
export default {
name: 'nameSearch',
data: function() {
return {
sectionTitle: 'Name Search'=
}
}
}
</script>
you may want to export the function from some other file, this is simply a case of declaring it the way you want.
in some other file...
// utils.js
export function createOptions (someArg) {
let options = [ ... ]
return options
}
in your .vue file
<script>
import { createOptions } from './utils'
export default {
...
data () {
return {
options: createOptions()
}
}
}
</script>
You may also want to try snovakovic's advice to externalise the dropdown component instead, whatever brings you more flexibility
Why not just create states components that handles drop-down and then include that component everywhere where it's used.
If you want just a function then create js file that expose that function and than import that file inside component.