Using vuejs and Cordova, I could not dynamicaly insert/create a new v-select component.
My goal is to duplicate and insert a copy an existing v-select when my "Add" button gets clicked.
The code of my page is:
<v-select
:items="exerciceList"
v-model="selectedExercice"
label="pick an exercise"
v-validate="'required'"
data-vv-name="select"
required>
</v-select>
</div>
<div id="selectsContainer"></div>
<v-btn flat icon id="btn" v-on:click="newExercice()">
My (not working) click event listener:
methods: {
newExercice: function () {
var container = document.getElementById('selectsContainer');
var select = document.getElementById('exo');
// select.items = this.exerciceList;
select.removeAttribute('id');
select.style.display = 'block';
container.appendChild(select);
}
},
The item with the id expo's code:
<v-select
id="exo"
style="display: none;"
:items="exerciceList"
v-model="selectedExercice"
label="Choisissez un exercice"
v-validate="'required'"
data-vv-name="select"
required>
Thank you for helping me improve my implementation.
What Michael S has said is correct, you do not want to manipulate the DOM directly, causes many issues. Probably the best solution (quick and easy anyway) would be to import that additional v-select component and then add it dynamically with a simple list or in this case an integer increase.
If you're wanting to get a little more customized with it, you can create an object and add and remove styles, attr, etc. from it dynamically the same way this option would work but turning item into an array of objects. then proceed to add an object to it whenever the button is clicked.
<v-select
:items="exerciceList"
v-model="selectedExercice"
label="pick an exercise"
v-validate="'required'"
data-vv-name="select"
required>
</v-select>
</div>
<div id="selectsContainer"
v-for="(item, idx) in items">
<v-select
id="item.id" <!-- or use idx -->
style="item.style" <!-- or use any string statement -->
...etc
>
</div>
<v-btn flat icon id="btn" v-on:click="newExercice()">
...{
components: [componentName],
data: return {
items: 0,
...
}
methods: {
newExercice: ()=>{
this.items++
}
}
...}
Related
I have one add button, on clicking that button one dropdown should add automatically, and then if I click it again it should again add that dropdown.
When one dropdown is added I chose one option from available options and then I clicked the add button again to add one more dropdown below it, the dropdown gets added but it is added with the option that I chose for the first dropdown.
Code:
HTML:
<div>
<button class="button btn-primary" #click="addRow">Add row
</button>
</div>
<div class="form-group row" v-for="(row,index) in addRowArr">
<div class="col-md-3">
<label>DropDown: </label>
<b-input-group left="<i class='fa fa-location-arrow'></i>">
<basic-select :options="SupplyChainArray" :selected-option="SupplyChaindata"
#select="SupplyChainobject" v-model="SupplyChain" name="SupplyChain"
id="SupplyChain" placeholder="Select SupplyChain">
</basic-select>
</b-input-group>
</div>
</div>
Javascript:
export default{
data(){
return(){
addRowArr : [],
SupplyChainArray : [{value:1, text:"B2C_HEAVIES"},
{value:2, text:"second"},
{value:3, text:"third"}],
SupplyChaindata: {},
SupplyChain: '',
}
},
methods:{
addRow(){
this.addRowArr.push({})
},
SupplyChainobject(obj) {
this.SupplyChaindata = obj;
this.SupplyChain = obj.value;
},
}
}
For reference please view the attached image :
It is because you are using a v-model with the same variable for all your dropdowns.
You have to use a different variable por each dropdown. According to your data model maybe SupplyChain must be an object with key->value where key is your SupplyChainArray.text and value is the variable associated to v-model.
<basic-select :options="SupplyChainArray" :selected-option="SupplyChaindata"
#select="SupplyChainobject" v-model="row.value" name="SupplyChain"
id="SupplyChain" placeholder="Select SupplyChain">
</basic-select>
I am developing a Vuejs application within which I have a field extension. For this field, users can provide the values and this field expands dynamically (both vertically and horizontally) based on the user-provided values.
I am using the recursive component ExtensionComponent to display and store the values. But it's not working as expected for me.
The field name extensions displays, initially an Add Extension. With on click of the button, a bootstrap modal will be displayed which has 3 fields: namespace (text), localname (text), datatype(dropdown: string/complex). If the datatype is string then a simple text field will be displayed. However, if the datatype is complex then another button should be displayed and on click of the button again the same bootstrap modal is displayed with fields and the process continues. So the created JSON based on this will expand horizontally and vertically.
I am able to create the Vue component to show the recursive data but I am a bit confused about the addition of the values to extensionList array dynamically. Can someone please help me with this issue?
Following is my Test.vue page which will display the Extensions and Add Button:
<template>
<div>
<span>Extensions</span>
<button class="btn btn-primary" #click="createExtensions">
Add Another
</button>
<ExtensionComponent :extension-list="$store.state.extensionList" />
<TestModal v-if="$store.state.showModal" />
</div>
</template>
<script>
import TestModal from '#/components/TestModal.vue'
export default {
computed: {
TestModal
},
methods: {
// Method to create extensions and add
createExtensions () {
this.$store.commit('toggleExtensionModal')
}
}
}
</script>
<style>
</style>
Following is my Bootstrap modal (TestModal.vue) which will be displayed whenever Add Another button is clicked (initially and for complex):
<template>
<b-modal
id="Extension"
title="Add Another Element"
size="lg"
width="100%"
:visible="$store.state.showModal"
>
<b-form id="AddExtension" #submit.prevent="submitExtension">
<div class="form-group">
<label for="message-text" class="col-form-label">Namespace URI:</label>
<input
v-model="extension.namespace"
type="text"
class="form-control"
required
>
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Local Name:</label>
<input
v-model="extension.localName"
type="text"
class="form-control"
required
>
</div>
<div class="form-group">
<label
for="AddExtensionDataType"
class="col-form-label"
>Data Type:</label>
<b-form-select v-model="extension.dataType" class="form-control">
<b-form-select-option value="string">
String
</b-form-select-option>
<b-form-select-option value="complex">
Complex
</b-form-select-option>
</b-form-select>
</div>
</b-form>
<template #modal-footer="{ cancel }">
<b-btn #click="cancel">
Cancel
</b-btn>
<b-btn variant="primary" type="submit" form="AddExtension">
OK
</b-btn>
</template>
</b-modal>
</template>
<script>
export default {
data () {
return {
extension: {
namespace: '',
localName: '',
dataType: 'string'
},
showModal: false
}
},
methods: {
submitExtension () {
// Call vuex store to save information
this.$store.commit('addExtension', this.extension)
// Hide modal after submitting modal
this.$store.commit('toggleExtensionModal')
}
}
}
</script>
<style>
</style>
Following is my ExtensionComponent.vue which will display data recursively:
<template>
<div>
<div
v-for="extension in extensionList"
:key="extension.ID"
class="form-inline"
>
<span>{{ extension.namespace + ":" + extension.localName }}</span>
<input
v-if="extension.dataType == 'string'"
type="text"
#input="$emit('AddExtensionText', {$event, id: extension.ID})"
>
<ExtensionComponent v-if="extension.dataType == 'complex'" :extension-list="extension" #AddExtensionText="AddExtensionText($event)" />
<button
v-if="extension.dataType == 'complex'"
#click="AddComplextExtension(extension.ID, extension)"
>
Add another
</button>
</div>
</div>
</template>
<script>
import ExtensionComponent from '#/components/ExtensionComponent.vue'
export default {
components: {
ExtensionComponent
},
props: {
extensionList: Array,
extension: Object
},
methods: {
AddComplextExtension (extensionID, extension) {
this.$store.commit('modules/ExtensionDataStore/showExtensionModal')
},
AddExtensionText ({ value, id }) {
const i = this.extensionList.findIndex(el => el.ID === id)
this.$set(this.extensionList, i, value)
}
}
}
</script>
Following is my index.js Vuex store:
export const state = () => ({
extensionID: 0,
extensionList: [],
showModal: false
})
export const mutations = {
toggleExtensionModal (state) {
// Function to show/hide the extension Modal
state.showModal = !state.showModal
},
addExtension (state, extension) {
console.log(extension)
extension.ID = state.extensionID
state.extensionList.push(extension)
state.extensionID++
}
}
export const actions = {}
Following is the front-end I have:
I want to create JSON and show it something like this:
As we can see if the value is complex then the field is created dynamically and displayed in a parent-child relationship way. I am a bit confused about how to establish the parent-child relationship within complex JSON and show them to the user. Someone,Ad
I have table which has some list of products, now for every product there is an upload file and a completed checkbox,
<vs-table :data="fileUpload">
<template slot="header">
<h3>
Upload Files
</h3>
</template>
<template slot="thead">
<vs-th>
Data
</vs-th>
<vs-th>
Upload Files
</vs-th>
<vs-th>
Uploaded
</vs-th>
</template>
<template slot-scope="{ data }">
<vs-tr :key="indextr" v-for="(tr, indextr) in data">
<!--<vs-tr>-->
<vs-td>
{{tr}}
</vs-td>
<vs-td>
<div class="centerx">
<input type="file" v-on:change="handleFileUpload($event,indextr)" />
</div>
</vs-td>
<vs-td>
<vs-checkbox v-model="completedCheckboxes" :vs-value="indextr"></vs-checkbox>
</vs-td>
</vs-tr>
</template>
</vs-table>
now on the change event of handleFileUpload I want to set the checkbox next to it as true.
handleFileUpload: async function(event,checkboxIndex) {
//set checkbox for that row here
}
data() {
return {
completedCheckboxes:[],
}
}
now i do get the checboxes value in completedCheckboxes array If i select them manually, but cant seem to work through code, I went over many solutions but nothing worked for me, any help?
Since the check box is the index try to push that uploaded file index to the completedCheckboxes array :
handleFileUpload: async function(event,checkboxIndex) {
this.completedCheckboxes.push(checkboxIndex)
}
Abc.vue
<v-flex xs12 sm6>
<v-btn class="changeNumberBtn" disabled #click="changeNumber()">Change Number</v-btn>
</v-flex>
<v-btn round block color="blue darken-3" dark large #click="generateCode">Continue</v-btn>
Currently Change Number button is disabled. How can I enable Change Number button when I click on Continue button ?
What is generateCode? If it has some logic, just toggle some bool value to it, like:
generateCode() {
this.toggleButton = true
//rest of your logic
}
<v-btn class="changeNumberBtn" :disabled="toggleButton" #click="changeNumber()">Change Number</v-btn>
You can conditionally disable/enable button as below.
var app = new Vue({
el: '#app',
data: {
disabled: true,
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button id="name" class="form-control" :disabled="disabled">Change Number</button>
<button #click="disabled = !disabled">Continue</button>
</div>
Keep a property in data say numberDisabled. Initialize it to true then on click of Continue, change toggle its value to false and in the changeNumber button set disabled value to this variable.
<v-flex xs12 sm6>
<v-btn class="changeNumberBtn" :disabled="numberDisabled" #click="changeNumber()">Change Number</v-btn>
</v-flex>
<v-btn round block color="blue darken-3" dark large #click="numberDisabled=false;">Continue</v-btn>
just to show the idea, I am changing the variable on #click, though you can move it inside generateCode method.
I'm trying to create a component which has two slots. The second slot is repeating based on the number of items in the first slot. I have achieved this using scoped slots, however, when the data is updated on the first slot, the second slot does not automatically update it until an event is triggered, eg: click of a button which calls a method.
Is there a way to make the second slot updates its view when the data is changed on the first slot?
Here is the example that I have:
Jsfiddle: https://jsfiddle.net/89vykm75/1/
new Vue({
el: '#app',
components: {
'repeat-for-each-item': {
data: function() {
return {
items: []
}
},
template: `<div>
<slot name="item" v-for="item in items" :item="item"></slot>
<button #click="addItem()">Add item</button>
<slot name="repeat" v-for="item in items" :item="item"></slot>
</div>
`,
methods: {
addItem() {
this.items.push({});
}
}
}
}
});
<div id="app">
<repeat-for-each-item>
<template slot="item" scope="props">
<div>
<input type="text" v-model="props.item.name">
</div>
</template>
<template slot="repeat" scope="props">
<div>
<label>
<span v-if="props.item.name">{{props.item.name}}:</span>
<span v-else>No Name:</span>
</label>
<input type="text">
</div>
</template>
</repeat-for-each-item>
</div>
I found a solution by calling a method on keyup.
Basically, I added #keyup event on the slot
<input type="text" v-model="props.item.name" #keyup="props.onchange()">
And on the component template, pass on the onchange method to the slot
<slot name="item" v-for="item in items" :item="item" :onchange="onchange"></slot>
And then have the onchange function to force the re-render
onchange:() => {
// hack to trigger changes
this.$set(this.items, 0, this.items[0]);
}
Here is the full working JsFiddle: https://jsfiddle.net/89vykm75/2/
I wonder if there is a cleaner solution?
You are falling into a Vue change detection caveat. The issue here is if you add a property to an object that didn't exist before when the object was added to the Vue data, then Vue cannot detect the change. Here is the problem:
this.items.push({})
You're adding an object with no properties, and then you bind v-model to the name property of that object, which does not exist. Vue cannot detect the change, and does not update the other items bound to that property.
If you instead did this:
this.items.push({name: null})
You will find your code works. Here is an updated fiddle.