Trigger submit with prevent default - vue.js

I'm trying to call submit method on form via refs from my script. On form tag I have #submit.prevent.
<template>
<div id="app">
<form ref="search-form" #submit.prevent="sub">
<input type="text" required />
</form>
<button #click="submitForm">click me</button>
</div>
</template>
<script>
export default {
name: "App",
methods: {
submitForm() {
this.$refs["search-form"].submit();
},
sub() {
console.log("submitted");
},
},
};
</script>
My problems are that when I click button:
page get reloaded like prevent default is not working
sub() is not called at all
Why?

Your question is not clear what you want to do but maybe you need this
const app = new Vue({
methods: {
submitForm() {
this.$refs["search-form"].submit();
},
sub() {
alert('hello');
},
}
})
app.$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form ref="search-form" #submit.prevent="sub">
<input type="text" required/>
<button #click.native.prevent="submitForm">Send</button>
</form>
</div>

<template>
<div id="app">
<form ref="search-form">
<input type="text" required />
</form>
<button #click.stop.prevent="processForm">click me</button>
</div>
</template>
<script>
export default {
name: "App",
methods: {
processForm() {
// validation here
this.$refs["search-form"].onsubmit = this.submitForm();
},
submitForm() {
// AJAX here
console.log("submitted");
},
},
};
</script>
⚠️ I assume that when you use prevent means you are going to manually submit the form at a later stage (maybe after client side validation). Then its either you use AJAX or Axios to submit the form to the server side. Calling submit() anywhere here will still reload your page.

Related

Trying to access the store in a main page and I'm getting the error

Main.vue:
<template>
<div>
<div class="home_page_header">
<h1>My Recipe</h1>
<button #click="toggleOpen">Add New Recipe</button>
</div>
<div v-for="recipe in $store.state.recipes" :key="recipe.Recipe_Name">
<h1 >{{recipe.Recipe_Name}}</h1>
<p>{{recipe.Ingredients}}</p>
<router-link :to="`/recipe/${recipe.Recipe_Name}`">
<button>View Recipe</button>
</router-link>
<router-view/>
</div>
<div>
<h1></h1>
<p></p>
</div>
<div class="popUp" v-show="openUp">
<div>
<label for="receipe_name">Recipe Name</label>
<input type="text" id="receipe_name" v-model="values.receipeName"/>
</div>
<div>
<label for="ingredients_name">Ingredients</label>
<input type="text" id="ingredients_name" v-model="values.ingredientsName" v-for="i in values.ingredientsRows" :key="i"/>
<button #click="addrows" >Add Ingredients</button>
</div>
<div><button #click="onSubmit">Submit</button></div>
<div><button #click="toggleClose">Close</button></div>
</div>
</div>
</template>
<script>
import store from '#/store/index.js'
export default {
data() {
return {
values:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
values_final:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
openUp : false
}
},
methods: {
toggleOpen(){
this.openUp = true
},
toggleClose(){
this.openUp = false
},
onSubmit(){
if(this.values.receipeName || this.values.ingredientsName == ''){
alert('enter the full details')
}
},
addrows(){
this.values.ingredientsRows++;
},
},
computed: this.$store.commit('Add_Recipes',{...values_final})
}
</script>
store:
import { createStore } from 'vuex'
export default createStore({
state: {
recipes:[
{
Recipe_Name: 'curd',
Ingredients:'xxxxxx'
}
]
},
mutations: {
Add_Recipes (state,recipe) {
state.recipes.push(recipe)
}
},
Error : app.js:340 Uncaught TypeError: Cannot read properties of undefined (reading '$store')...
I'm trying to create a recipe app by using option API, I have one main page. In that main page that contains one title and add recipe button to add the details. And another one is a popup to enter the recipe details. so here after entering all the details that should show in a main page. I'm trying to access the store in a main page but iam getting the above error.
I guess you need to move the import statement of your store to your main.js file (not your Main.vue). And add app.use(store) after you created the app there with const app = createApp(…).

How do you use components with data from Nuxt Content Module

A component that has a hidden input field should have a the value of the current page title. In Nuxt you can accomplish it using asyncData for all pages except for pages using Nuxt Content Module. The Nuxt Content Module does not allow asyncData but fetch is allowed but. Example:
components/Form.vue
<template>
<div>
<input
id="page"
type="hidden"
:value="article.title"
required
/>
</div>
</template>
<script>
export default {
async fetch() {
this.article = await this.$content('articles', this.params).fetch()
},
data() {
return { article: {} }
},
}
</script>
pages/articles/_slug.vue
<template>
<div>
<h1>{{ article.title }}</h1>
<Form />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug).fetch()
return { article }
},
}
</script>
This code gets no errors but the component never shows page title in the hidden input only in the h1 tag. (edit: fixed typo)
How I solved this was with props.
components/Form.vue
<template>
<form>
<label for="title"> Title </label>
<input name="title" type="hidden" :value="title" required />
<button type="submit">Send</button>
</form>
</template>
<script>
export default {
props: {
title: {
type: String,
required: false,
default: ''
},
}
}
</script>
and then in the pages/_slug.vue
<template>
<div>
<nuxt-content :document="doc" />
<Form :title="doc.title" />
</div>
</template>
This should work for you.

Vue instance's $on method is not work

I just created an event bus in the main.js file like this:
main.js
Vue.prototype.$bus = new Vue()
After that, I just wrote some code to test the event bus like this:
TestComponent
<template>
<div>
<div class="account-modal_form">
<form action="" #submit.prevent="formSubmit">
<div class="account-modal_form__group" :class="{ warning: errors.has('password') }">
<div class="account-modal_form__input">
<input name="password" :type="passwordType" placeholder="" class="width-316" v-validate="'required'" v-model="password">
<i class="account-modal_form__viewpass" #click="togglePassword"></i>
</div>
<span class="account-modal_form__warning" v-show="errors.has('password')">
{{ errors.first('password') }}
</span>
</div>
{{ errors }}
<div class="account-modal_form__group">
<button type="submit" class="btn btn--primary btn--large">next</button>
<button type="button" class="btn btn--default" #click="cancelAction">cancel</button>
</div>
</form>
</div>
</div>
</template>
<script>
import { API } from '#/api'
export default {
data() {
return {
passwordType: 'password',
password: ''
}
},
methods: {
created() {
this.$bus.$on('test', () => console.log('test'));
},
nextStep() {
this.$bus.$emit('test');
},
formSubmit() {
this.nextStep();
}
}
}
</script>
When I click submit button I want to submit form first and call nextstep to emit an event, but the $on event output nothing.
You're running $emit before $on, so when you fire the event there are no listeners at that point, and it's better to register your listeners on the component created life cycle event, otherwise whenever you run your test method you'll register a new listener:
Vue.prototype.$bus = new Vue();
Vue.component('spy-component', {
template: '<p>{{this.text}}</p>',
data() {
return {
text: '',
}
},
created() {
this.$bus.$on('sendOriginPassword', (text) => {
this.text = text;
});
}
})
Vue.component('test-component', {
template: '<button #click="test">Click me</button>',
created() {
this.$bus.$on('sendOriginPassword', () => {
console.log('I am listening event')
});
},
methods: {
test() {
this.$bus.$emit('sendOriginPassword', 'Can you hear me?');
}
}
});
new Vue({
el: "#app",
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<spy-component></spy-component>
<test-component></test-component>
</div>

Custom Error Message in Laravel 5.6.7 and Vue.js in particular Component

I am using Laravel 5.6.7 and vue.js
Validation Message
When the validation works in vue.js, it says "The User Name field is required.". Can it be "Please enter user name"? Can I use two custom error messages for same field? Example
Required: Please enter username.
Alpha: Please enter alpha chars only.
I am using vee-validate Package for Form Validation
Below is the code in blade
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-12">
<create-user></create-user>
</div>
</div>
</div>
Code in vue.js Component.
<template>
<form role="form" class="row">
<input name="User Name" v-validate data-vv-rules="required|alpha" type="text"
v-model="createForm.UserName">
<p v-if="errors.has('User Name')">{{ errors.first('User Name') }}</p>
<button type="button" #click="validateBeforeSubmit()" class="btn btn-primary">
Create
</button>
</form>
</template>
<script>
export default {
data() {
return {
createForm: {
UserName: ''
}
};
},
methods: {
validateBeforeSubmit() {
this.$validator.validateAll();
}
}
}
</script>
To achieve what you want you can use Field Specific Custom Messages.
Not much to it, you just create a customMessages dictionary and add it to your validator using .localize().
So, nothing in your template changes, but in your <script> you would declare the customMessages and apply it in the created() lifecycle hook, as shown below and in the demo:
<script>
const customMessages = { // added
custom: { // added
'User Name': { // added
required: 'Required: Please enter username', // added
alpha: 'Alpha: Please enter alpha chars only.' // added
} // added
} // added
}; // added
export default {
data() {
return {
createForm: {
UserName: ''
}
};
},
created() { // added
// change 'en' to your locale // added
this.$validator.localize('en', customMessages); // added
}, // added
methods: {
validateBeforeSubmit() {
this.$validator.validateAll();
}
}
}
</script>
Runnable Demo:
Vue.use(VeeValidate);
const customMessages = {
custom: {
'User Name': {
required: 'Required: Please enter username',
alpha: 'Alpha: Please enter alpha chars only.'
}
}
};
Vue.component('create-user', {
template: '#create-user-template',
data() {
return {
createForm: {
UserName: ''
}
};
},
created() {
// change 'en' to your locale
this.$validator.localize('en', customMessages);
},
methods: {
validateBeforeSubmit() {
this.$validator.validateAll();
}
}
});
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vee-validate#latest/dist/vee-validate.js"></script>
<template id="create-user-template">
<form role="form" class="row">
<input name="User Name" v-validate data-vv-rules="required|alpha" type="text"
v-model="createForm.UserName">
<p v-if="errors.has('User Name')">{{ errors.first('User Name') }}</p>
<button type="button" #click="validateBeforeSubmit()" class="btn btn-primary">
Create
</button>
</form>
</template>
<div id="app">
To see the 'required' message: type "x", then delete<br>
To see the 'alpha' message: type "_"<br><br>
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-12">
<create-user></create-user>
</div>
</div>
</div>
</div>
that is will not work use ajax to get data
between vuejs and server

custom v-focus not work when there are many v-if in parent node

I define the custom directive "focus" in my component:
<script>
export default {
name: 'demo',
data () {
return {
show: true
}
},
methods: {
showInput () {
this.show = false
}
},
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
}
And this is my html template:
<template>
<div>
<input type="number" id="readonly" v-if="show">
<button type="button" #click="showInput" v-if="show">show</button>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</template>
But when I click the button, input#timing can't autofocus.
When I put input#readonly and button into a div and use only one v-if, input#timing can be autofocus:
<template>
<div>
<div v-if="show">
<input type="number" id="readonly">
<button type="button" #click="showInput">show</button>
</div>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</template>
This is why???
The directive's code is indeed running and focusing the <input>.
But it is being removed from the DOM! When this happens, it loses focus. Check the console of the fiddle below: https://jsfiddle.net/acdcjunior/srfse9oe/21/
Another important point is that, when inserted is called, the <input id="timing"> is in the DOM (as mentioned above), but it is in the DOM at the wrong location (between <p>a</p> and <p>b</p> where it was never supposed to be). This happens because Vue tries to reuse elements.
And when the nextTick triggers (see fiddle), it is in its correct placement (between <p>c</p> and <p>d</p>), because Vue moved it to the correct location. And is this moving that is taking focus out.
And because nextTick runs after the DOM moving around is done, the focus persists (see below).
Using Vue.nextTick():
Defer the callback to be executed after the next DOM update cycle. Use
it immediately after you’ve changed some data to wait for the DOM
update.
new Vue({
el: '#app',
data() {
return {
show: true,
timing: 123
}
},
methods: {
showInput() {
this.show = false
}
},
directives: {
focus: {
inserted: function(el) {
Vue.nextTick(() => el.focus()); // <======== changed this line
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div>
<input type="number" id="readonly" v-if="show">
<button type="button" #click="showInput" v-if="show">show</button>
<input type="number" id="timing" v-model="timing" v-if="!show" v-focus>
</div>
</div>