I am trying to render different form elements (placeholder and submit action) in a bootstrap vue form. Here is the form:
<template>
<b-form-input
v-if="input"
placeholder="Name Your Swatch, Enter and Save Edit"
#keypress="republishSwatch"
v-model="value3"
ref="value3"
id="name"
size="lg"
type="text"
class="search-bar"
/>
<b-form-input
v-else
placeholder="Name Your Swatch and Enter to Save"
#keypress="publishSwatch"
v-model="value3"
size="lg"
ref="value3"
id="name"
type="text"
class="search-bar"
/>
</template>
This works, and the default form shows, collects the name etc and runs the publishSwatch function. The other form I want to use when someone is editing the swatch and changing the colors/name, using either the setName or the republishSwatch function so need some kind of condition in there to get it to show instead of the default one. At this point the the Save Edit (id saveBtn) button is showing in the DOM (shows after clicking the edit button) so wondered if I could somehow use that as a reference in condition, as its not there normally. The condition there at the moment doesn't seem to do anything, the default form shows, which is what I want to start with.
Any tips welcome
Thanks
Because your 2 situations(v-if/v-else) are very different, you should duplicate your b-form-input, by this way, you give v-if to the 1st one and v-else to the other one.
<template>
<b-form-input
v-if=""
placeholder="Name Your Swatch and Enter to Save"
#keypress="publishSwatch"
/>
<b-form-input
v-else
placeholder="Name Your Swatch, Enter and Save Edit"
#keypress="republishSwatch"
v-model="value3"
ref="value3"
id="name"
size="lg"
type="text"
class="search-bar"
/>
</template>
With simpler examples, i.e. only a the placeholder which changes, you could do that
<template>
<b-form-input
:placeholder="test ? placeholder1 : placeholder2"
/>
</template>
<script>
export new Vue({
data() {
return {
test: true,
placeholder1: 'hi',
placeholder2: 'bye',
};
}
});
</script>
If you mutate test, the input placeholder will automatically change.
Related
I am using Bootstrap-Vue Modals to open an "Edit" modal when clicking the "Edit" button that is attached to each item I have rendered in a list from v-for rendered list.
Each time, however, when I click on the edit button, it opens all of the modals, stacked on top of eachother with the last element in the list being the top modal.
How can I specify it to only open the modal/information for the item that is clicked to be edited?
//Parent Component
<div class="dataList">
<div v-bind:key="item.id" v-for="item in this.$store.getters.data">
<Child v-bind:item="item"></Child>
</div>
</div>
//Child Component
<div>
{{this.item.name}}
{{this.item.details}}
{{this.item.completedBy}}
{{this.item.status}}
<button v-b-modal.modal-1>Edit</button>
<button v-on:click="deleteItem">Delete</button>
<div>
<b-modal id="modal-1" title="BootstrapVue">
<form #submit="editItem">
<input v-model="name">
<input v-model="details">
<input v-model="completedBy">
<select v-model="status">
<option>Fail</option>
<option>Warn</option>
<option>Pass</option>
</select><br>
<input type="submit" value="Submit" #click="$bvModal.hide('modal-1')">
</form>
</b-modal>
</div>
</div>
Now each modal shows the correct information (like the proper name, details, status, etc), but I just need it only the specific modal.
I imagine it has something to do with the 'v-b-modal.modal-1' but I'm not sure how to dynamically set the id of each modal...is there a way to easily set each modal id to match the item.id?
Here is the documentation for Bootstrap-Vue Modals, but I wasn't to find what I needed.
I'd recommend moving the <b-modal> out of your v-for.
This way you only have one.
Then you instead set the user/item you want to edit to a variable, and utilize that in your modal to show the data for the selected user.
Example
new Vue({
el: "#app",
data() {
return {
selectedUser: null,
items: [{
name: "Name 1",
status: "Fail",
details: "Details 1"
},
{
name: "Name 2",
status: "Warn",
details: "Details 2"
}
]
}
},
methods: {
editUser(user) {
this.selectedUser = user;
this.$bvModal.show("edit-modal");
}
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" />
<link href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="//unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app" class="p-4">
<div v-for="item in items" class="mt-3">
{{item.name}} {{item.details}} {{item.status}}
<b-btn variant="primary" #click="editUser(item)">Edit</b-btn>
<b-btn variant="danger">Delete</b-btn>
</div>
<b-modal id="edit-modal" title="Edit User">
<form v-if="selectedUser">
<input v-model="selectedUser.name">
<input v-model="selectedUser.details">
<input v-model="selectedUser.completedBy">
<select v-model="selectedUser.status">
<option>Fail</option>
<option>Warn</option>
<option>Pass</option>
</select><br>
<input type="submit" value="Submit">
</form>
</b-modal>
</div>
If you want to keep the modal inside your v-for, you will have to give it a unique id. If you item has one i would recommend using that, otherwise you can use the index from the v-for.
Simplified example
<div v-for="item in items">
<button v-b-modal:[`edit-modal-${item.id}`]>Edit Item</button>
<b-modal :id="`edit-modal-${item.id}`">
<!-- Modal Content-->
</b-modal>
</div>
One of the most quick way but maybe not the best is Conditional Rendering the modals.
You can toggle the active modal number/index when the Edit is clicked by setting a prop/data/var as activeModal: 1 or n.
Then at the section where you iterate modals you can use v-if directive for modal like: <v-modal v-if="activeModal"> that'd be one way to ignore/not render other modals but would cost re-rendering on each Edit click.
Best/Final solution would be to vue-portal the content to a single universal modal.
I have a background in Angular. Starting with vue was an okay experience for me until I came across a problem which VueJS developers seem to have shit on and slid under the carpet.
How can we create a form in which user can press enter from an input field to submit the form
This was seriously disappointing.
and please if you know the answer be kind enough to post in the Official vue documentation as well.
*Note:
my workaround: I used v-on:keydown.enter.prevent='loginUser' on every input field.
is there any way to not use it this way ( on every input field).
With button type as submit as well, form gets submitted when Enter key is pressed on any input.
No explicit binding is required on keypress.
new Vue({
el: '#app',
data() {
return {
title: "Vue 2 -Form submission on Enter",
formInput:{
fname:'',
lname:'',
gender:'male'
}
};
},
methods:{
onSubmit(){
console.log('submitted', this.formInput)
}
}
})
.form{
display:flex;
flex-direction:column;
gap:10px;
max-width:200px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"><h2>{{title}}</h2>
<form v-on:submit.prevent="onSubmit" class="form">
<input v-model="formInput.fname" #keydown.enter.prevent placeholder="first name"/>
<input v-model="formInput.lname" placeholder="last name"/>
<div>
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="male"/>male
<input v-model="formInput.gender" name="gender" placeholder="Gender" type="radio" value="female"/>Female
</div>
<button type="submit">Submit</button>
<pre>{{formInput}}</pre>
</form>
</div>
I'd urge you to check out this page on the VueJS documentation.
It explains how to set events on certain interactions. For example, you can trigger a function call on pressing the Enter key within an input field by doing this:
<input type="text" #keyup.enter="submit">
This will call the submit() method when the Enter key is pressed and released (keyup). On press, you can use keydown instead.'
In fact, the example I've taken is directly from this section in the page I linked above.
EDIT: A pure-HTML way to do this is to set your input type as submit, which will allow Enter to submit the form
You need to wrap your inputs within <form> ... </form> tag
I am using Vue js right now and I am trying to duplicate some codes in template. As I do not know how many form it needs so it must be flexible.
My template code
<form #submit="duplicated">
<b-form-input id="name" v-model="name" type="text" required/>
... other codes needs to copy
<b-button type="submit">Save</b-button>
<!-- after clicking the button, the website should show two forms -->
</form>
You can store a variable in data and use v-for in your form.
<form v-for="(form,index) in numberofForms" :key="index"
#submit.prevent = "numberofForms++" >
<b-form-input id="name" v-model="name" type="text" required/>
... other codes needs to copy
<b-button type="submit">Save</b-button>
</form>
export default {
...
data() {
return {
numberofForms: 1,
}
},
};
I'm trying to make two radio button clusters on the same page (but inside different drawers) keep in sync with each other. This worked before I broke them out into a component.
Now my component looks like:
Vue.component('radio-cluster', {
props: ['value'],
template: `
...
<input type="radio" name="radio" value="Hour" :checked="value == 'Hour'" #input="$emit('input', $event.target.value)">
<input type="radio" name="radio" value="Week" :checked="value == 'Week'" #input="$emit('input', $event.target.value)">
...
`,
});
The Vue instance:
new Vue({
el: '#app',
data: function () {
return {
timePeriod: "Hour"
}
});
And the relevant HTML:
<div id="app">
<radio-cluster v-model="timePeriod"></radio-cluster>
...
<radio-cluster v-model="timePeriod"></radio-cluster>
</div>
The behavior I'm getting is odd. It will correctly show in one of the two clusters, and emit the correct event from either one. But the other component ignores the event from the one I clicked on. How can I make Vue take the value updated in one component and automatically give it to the other every time either one updates?
Seems like the cause of you problem is the same name for each input.
From documentation
A radio group is defined by giving each of radio buttons in the group the same name. Once a radio group is established, selecting any radio button in that group automatically deselects any currently-selected radio button in the same group.
Try to remove name attribute:
<input type="radio" value="Hour" :checked="value == 'Hour'" #input="$emit('input', $event.target.value)">
<input type="radio" value="Week" :checked="value == 'Week'" #input="$emit('input', $event.target.value)">
if name attribute is required use different names for each component.
Thanks #Ferrybig for the comment.
So I currently have a template sitting in a ".vue" file like so:
<template>
<div id="dataAttachToMe"></div>
</template>
I don't want this to load, unless a user clicks a button, something like
<button #click="loadTheTemplateAbove">See Data</button>
I've tried using this example:https://v2.vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key. But it says something like "Component template should contain exactly one root element" in the error message.
I need more than a show/hide here I think, something that can initiate the template dynamically.
<template>
<div id="data">
<button #click="loadTemplate">Load the template</button>
<div v-if="buttonClicked">
<div id="dataAttachedToThisDiv"></div>
</div>
</div>
</template>
The error you are getting, means that there is more than one root element inside <template></template> tag.
It is required in Vue.js (and other template based frameworks/libraries) to have only one root element.
This will NOT work:
<template>
<div id="dataAttachToMe"></div>
<button #click="loadTheTemplateAbove">See Data</button>
</template>
This will work:
<template>
<div id="someRootDiv">
<div id="dataAttachToMe">Some data</div>
<button #click="loadTheTemplateAbove">See Data</button>
</div>
</template>
Here is a code example (App.vue) of what you are trying to achieve:
Basic idea: we have to create a variable, that will be changed upon button click. We add v-if directive that depends on that variable and will handle element's visibility.
Welcome to StackOverflow. When you get the error Component template should contain exactly one root element it means that you can only have one root element in your template. You can fix that error by wrapping everything in a blank div like so
<template>
<div>
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
</div>
</template>
Please edit your post and place you <script> tag. Conditional Rendering requires a data field of a boolean that you can place in your if statement on your template
<template>
<div>
<div v-if="show">{{message}}</div>
<div v-if="#show">Not Showing when show is set to false</div>
<button v-on:click="show = true">Show</button>
</div>
</template>
<script>
module.exports {
data: function () {
message: 'Hello Vue!',
show: false
}
}
</script>