Vue submit input hidden - javascript

I want to pass two Id's alongside a users input. I'm putting those Id's in a hidden input. I already understand that the v-model does not work with hidden inputs. That's why my hidden input looks like this
<input type="hidden" ref="property_id" :name="form.property_id" :value="property_id"><
The following is my full block of code:
<template>
<div>
<form #submit.prevent="submit()">
<div v-for="(property, propIndex) in properties" :key="propIndex">
{{ property.name }}
<div v-for="(house_type, typeIndex) in property.house_type.data" :key="typeIndex">
{{ house_type.type }}<br>
<input type="text" v-model="rent[propIndex][typeIndex]">Rent<br>
<input type="text" v-model="house_quantity[propIndex][typeIndex]">How many<br>
<input type="hidden" ref="property_id" :name="form.property_id" :value="property_id"><br>
<input type="hidden" ref="house_type_id" :name="form.house_type" :value="house_type.type"><br>
</div>
<br>
</div>
<button>Submit</button>
</form>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
data() {
return {
rent:[{}, {}, {}, {}, {}, {}],
house_quantity:[{}, {}, {},{}, {}, {},{}, {}, {}],
form:{
property_id: [],
house_type: [],
}
}
},
}
</script>
How do I get the values of the hidden inputs so that I can submit them alongside the rest of the data.
methods: {
async submit(){
this.form.rent = this.rent
this.form.house_quantity = this.house_quantity
await axios.post('/api/landlord/set/up/store/part/3', this.form)
}
},

If I got it right, you can use v-bind to bring the values to the input and make a method or computed property that gets the values back to this.form.property_id and this.form.house_type when input #blur or #change, for example.
This would update data back even without having the v-model interactivity you need.

Related

Adding new item to an array of a reactive object replaces all items in the array. Why?

The main problem is in the title. My guess would be that the ref() and reactive() are somehow messing with each other, I just don't know how and what I could do about it.
The component using the store:
<script setup>
import { ref } from "vue";
import { store } from "#/store.js";
const game = ref({
date: null,
location: null,
length: null,
});
</script>
<template>
<form #submit.prevent="store.addGame(game)">
<input v-model="game.date" type="date" required />
<input v-model="game.location" type="text" placeholder="Location" required />
<input v-model="game.length" type="number" placeholder="Length (in minutes)" required />
<button class="button" type="submit">Submit ✔</button>
</form>
</template>
The problem occurs in the store, in the games array, after I add a second or more items...
store.js:
import { reactive } from "vue";
export const store = reactive({
games: [],
addGame(game) {
this.games.push(game);
console.log(this.games);
}
});
The reason is because all the objects that you push inside games share the same reference. To fix this, do this change in store.js
import { reactive } from "vue";
export const store = reactive({
games: [],
addGame(game) {
this.games.push({...game}); // line changed
console.log(this.games);
}
});
It's not "replace", all the game reference to the same reactive variable defined in const game = ref().
Whenever you call store.addGame(game), you added the same game to the store. Because the game is reactive, is will automatic implement the latest input value by v-model.
If you put another button to check the value in store. Change the input value, and then click Test button, you can see the store.games changes too.
<template>
<form #submit.prevent="store.addGame(game)">
<input v-model="game.date" type="date" required />
<input v-model="game.location" type="text" placeholder="Location" required />
<input v-model="game.length" type="number" placeholder="Length (in minutes)" required />
<button class="button" type="submit">Submit ✔</button>
</form>
<button #click="console.log(store.games)">Test</button>
</template>

Is it possible to add a vue component to a traditional html form?

Is it possible to add a vue input component to a standard html form? I know this is not the ideal way to handle vue forms, but I'm curious about the possibility as a "quick and dirty" way for adding custom elements into pre existing html forms. Here's a hypothetical example:
<form action="/users" accept-charset="UTF-8" method="post">
<input type="email" name="user[email]" />
<input type="password" name="user[password]" />
<my-custom-fancy-vue-component />
<input type="submit"value="Sign up">
</form>
I'm wondering if the browser can read the value exposed by an input element withen the vue component and send that as a param when the user submit the form. Is there any other way to tell the browser how to access the value from a vue component if for example it doesn't use a native input internally, perhaps using a web component as a wrapper or using shadow dom?
Any <input> elements within the form should be included by the browser when the form is submitted. The browser won't care that the <input> is inside a Vue component.
For components that don't already have an <input> (or other suitable form element) you can add a hidden input, <input type="hidden">, to hold the value.
If the component you want to include is a third-party component then you won't be able to add the hidden input directly. However, you could still add it by using a wrapper component. The example below illustrates how you could handle that scenario.
const thirdPartyComponent = {
template: `
<button
#click="onClick"
type="button"
>
Increment {{ value }}
</button>
`,
props: ['value'],
methods: {
onClick () {
this.$emit('input', this.value + 1)
}
}
}
const myCustomFancyVueComponent = {
template: `
<div>
<third-party-component v-model="counter" />
<input type="hidden" :value="counter">
</div>
`,
components: {
thirdPartyComponent
},
data () {
return {
counter: 4
}
}
}
new Vue({
el: 'form',
components: {
myCustomFancyVueComponent
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<form action="/users" accept-charset="UTF-8" method="post">
<input type="email" name="user[email]">
<input type="password" name="user[password]">
<my-custom-fancy-vue-component></my-custom-fancy-vue-component>
<input type="submit" value="Sign up">
</form>

Vue store slot data in parent component

I'm building a component which takes in multiple input fields using a slot, when the user submit's the form inside the parent component, I want to output the values of all the inputs.
index.html
<filter-form>
<input type="text" name="email" :value="form.email" />
</filter-form>
FilterForm.vue
<template>
<div>
<form
#submit.prevent="onSubmit"
>
<slot />
<div>
<button>
Submit
</button>
</div>
</form>
</div>
</template>
<script>
export default {
data () {
return {
form: {
email: 'test#email.com'
}
}
}
}
</script>
As you can see I'm referencing "form.email" inside index.html, which should populate the input with the data in the FilterForm component, however that's throwing this error
Property or method "form" is not defined on the instance but referenced during render
Which makes sense since the form data isn't available in the index.html file.
How can I use this system so that the data for the form is kept in the FilterForm component, but I can add any numbe of form inputs to the index.html file.
As #User 28 mentioned, you can take advantage of slots and scoped-slots to get this to work with an arbitrary number of fields like so:
<template>
<div id="app">
<FormWrapper :names="['name', 'country']" #submit="process($event)">
<template #default="{ form }">
<input name="name" type="text" v-model="form.name">
<input name="country" type="text" v-model="form.country">
</template>
</FormWrapper>
</div>
</template>
<script>
import FormWrapper from "./components/FormWrapper";
export default {
name: "App",
components: {
FormWrapper
},
methods: {
process(values) {
console.log(values);
}
}
};
</script>
Here, FormWrapper can nest arbitrary input fields. Some key points:
You need to provide names prop with some unique IDs for fields you care about.
When form inside FormWrapper is submitted, a submit event will be triggered with the values sent as an object.
Also note that we pass data for our slots in a form object and bind it to our inputs via v-model.
Here is the FormWrapper component:
<template>
<div class="form-wrapper">
<slot :form="form"></slot>
<div class="controls">
<button #click="submit()">Submit</button>
</div>
</div>
</template>
<script>
export default {
props: {
names: Array
},
data() {
return {
form: {}
};
},
created() {
for (const name of this.names) {
this.$set(this.form, name, "");
}
},
methods: {
submit() {
this.$emit("submit", this.form);
}
}
};
</script>
You can check https://codesandbox.io/s/so-form-wrapper-yxz0i for a working example.
Vue has a feature called Scoped Slots which might solve your problem.
<filter-form>
<template v-slot='slotProps'>
<input :value='slotProps.form.email' />
</template>
</filter-form>
and
<div>
<form #submit.prevent=''>
<slot :form='form'/>
<div>
<button>Submit</button>
</div>
</form>
</div>
Example

Why is my boolean always true when passing it as value to a radio component?

so I am learning vue and have spent some time going through the documents and haven't seen the answer that solves my question. A lot of this is due to the nomenclature between using the CLI(which I am) and not.
I am trying to make it so that when one radio button is clicked it shows a div and when the other one is clicked it shows the other. Here is what I have.
Template:
<div id="daySelection">
<div class="o-m-day">
<div id="oneDay">
<p>One day?</p><input v-model="selected" type="radio" name="oneDay" id="" class="r-button" value="true">
</div>
<div id="multipleDays">
<p>Multiple days?</p> <input v-model="selected" type="radio" name="multDays" id="" class="r-button" value="false">
</div>
</div>
<!-- the div where the conditional render will be rendered -->
<div>
<!-- multiple days -->
<div v-show="selected" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
<!-- one day -->
<div v-show="!selected" id="i-oneDay">
<input type="text" name="r-oneDay">
</div>
</div>
</div>
Here is the script:
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: Boolean,
}
},
}
above I was getting an error in the console that was saying that data needs to be a function that returns a new instance. I see many people and examples using vue instances differently where it is:
const app = new Vue({
el: '#app',
data: {
selected: true,
}
});
However whenever trying this Vue sends me a warning saying that it needs to be a function.
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
I am also aware that v-show toggles the display so I have tried both setting the display of the divs to:
display: none;
as well as not.
The problem is that the value of selected is a string, whereas you expect it to be a boolean.
The following watcher:
watch: {
selected(newValue) {
console.log("selected changed to", newValue, "which is a", typeof newValue);
}
}
Will tell you this:
selected changed to true which is a string
selected changed to false which is a string
The reason is that you give the fields value a string instead of a boolean. To fix this, instead of writing value="true", write :value="true".
You can play with a live example here.
There are two problems as far as I can see here:
In a component, the data key must be a function and the value for the selected key in the object returned by the data function must be an actual boolean value true or false (which will be initial value)
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: true,
}
},
}
By default, v-model values are treated as strings so your true and false values are actually the strings "true" and "false" which are both truthy. Changing your template code to the below (or alternatively using a number or string value instead) should fix it
<div v-show="selected === 'true'" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
I solved it by changing it from a 'v-show' to 'v-if'
<div>
<p>One day?</p>
<input
v-model="selected"
type="radio"
name="oneDay"
id="oneDay"
class="r-button"
value="onlyOneDay" />
</div>
<div id="multipleDays">
<p>Multiple days?</p>
<input
v-model="selected"
type="radio"
name="multDays"
id="multDays"
class="r-button"
value="multipleDays" />
</div>
then the div to be shown as follows:
<div v-if="selected === 'multipleDays'" id="ta-multDays">
<textarea rows="10" cols="80" name="" type="text" />
</div>
<div v-if="selected === 'onlyOneDay'" id="i-oneDay">
<input type="text" name="">
</div>

Checkbox array in Vue Js

I have an array of checkboxes, coming from a main system object where I store all system setting. (called getSystem{}).
In this form, Im accessing a User, which has an array of roles [].
How can I check this array of roles, against the getSystem.user_roles?
I know how to do it normally, in javascript obviously. But what would I put in the checkbox input Vue.js wise?
<b-form-group>
<label for="company">Email Address</label>
<b-form-input type="text" id="email" v-model="user.email" placeholder="Enter a valid e-mail"></b-form-input>
</b-form-group>
// Here i can do user.roles to get the array of roles.
// What can I do to loop through the roles and check the box if it exists in the user roles??
<b-form-group v-for="resource, key in getSystem.user_roles" v-if="getSystem.user_roles">
<label>{{resource.role_name}}</label>
<input type="checkbox" [ what do I put here to compare against user.roles, and check the box if exists??] >
</b-form-group>
This behavior is well documented on the Checkbox binding Docs.
Here a little example emulating your logic
new Vue({
el: '#app',
data: {
user: {
email: 'test#test.com',
roles: [{id: 1, name: 'Client'}]
},
roles: [
{
id: 1,
name: 'Client',
},
{
id: 2,
name: 'Admin',
},
{
id: 3,
name: 'Guest',
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<div>
<label>Email</label>
<input type="text" v-model="user.email" />
</div>
<div v-for="role in roles" :key="role.id">
<label>{{role.name}}</label>
<input type="checkbox" v-model="user.roles" :value="role"/>
</div>
<p>User's selected roels</p>
{{user.roles}}
</div>
<input type="checkbox" v-model="userRoles" :true-value="[]" :value="resource.role_name">
You should add :true-value="[]".
<b-form-group v-for="resource, key in getSystem.user_roles" v-if="getSystem.user_roles" :key="key">
<label>{{resource.role_name}}</label>
<input type="checkbox" v-model="userRoles" :value="resource.role_name" >
</b-form-group>
<script>
data(){
return {
userRoles: []
}
}
</script>
We can use dynamic check box input rendering with the condition to select values from the customized function (in my example selectUsers). In that function, we can write conditions that we need to compare before append to the selected array.
Demo:
This is the full NuxtJs(vue) component with dummy data.
<template>
<v-container fluid>
<p>{{selected }}</p>
<div v-for="user in user_roles" :key="user[0]">
<input type="checkbox"
#click="()=>{selectUsers(user[0])}"
:value="user[0]"
>
{{user[1]}}
</div>
</v-container>
</template>
<script>
import VueLodash from 'vue-lodash'
import lodash from 'lodash'
export default {
data() {
return {
user_roles:[[1,"dd"],[2,"ddd"],[3,"kksse"],[4,"kske"]] ,
selected:[],
};
},
methods: {
selectUsers(id){
//in here you can check what ever condition before append to array.
if(this.selected.includes(id)){
this.selected=_.without(this.selected,id)
}else{
this.selected.push(id)
}
}
}
}
</script>
In my case, as I am server rendering the checkboxes, what worked for me is to replace :value with just value
#foreach($carOptions as $option)
<div class="form-check">
<input class="mx-2 form-check-input cursor-pointer"
type="checkbox"
value="{{$option}}" <------ HERE in order for vue to work I had to change from :value to value
v-model="offer.carOptions">
<label class="custom-control-label cursor-pointer"
for="leading">{{ __($option) }}</label>
</div>
#endforeach
NOTE: The code snippet is a Laravel Blade Templates.

Categories

Resources