Form data should remain after displaying error - laravel-8

I am new to inertia js and creating a project using Laravel and inertia js, I am facing an issue that form data is erased after any error display. How to prevent that in inertia.
I have already tried preserveState.
Vue code:
<template>
<template #breadcrumb>
<div>
<div>
<button #click="submit">Save</button>
</div>
</div>
</template>
<div>
<div>
<label>Name</label>
<t-input type="text" v-model="form.name" />
<div v-if="errors['name']">{{ errors["name"] }}</div>
</div>
<div>
<label>Comment</label>
<t-input type="text" v-model="form.comment" />
<div v-if="errors['comment']">{{ errors["comment"] }}</div>
</div>
</div>
</template>
<script>
import { Inertia } from '#inertiajs/inertia'
export default {
components: {
Inertia
},
props: {
form: Object,
errors: Object,
},
methods: {
submit() {
Inertia.post(this.route("post.store"), this.form, { preserveState: true });
},
},
};
</script>
Controller code
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string'],
'comment' => ['required', 'string'],
]);
$data = [
'name' => $request->get('name'),
'comment' => $request->get('comment'),
];
Post::create($data);
return redirect(route('post'))->with('success', 'Post created.');
}

Try using the form helper (read up on it here https://inertiajs.com/forms) something like this in your Vue component binding on the form props, not the prop you pass into the vue:
<template>
<template #breadcrumb>
<div>
<div>
<button #click="submit">Save</button>
</div>
</div>
</template>
<div>
<div>
<label>Name</label>
<t-input type="text" v-model="myForm.name" />
<div v-if="myForm.error('name')">{{ myForm.error('name') }}</div>
</div>
<div>
<label>Comment</label>
<t-input type="text" v-model="myForm.comment" />
<div v-if="myForm.error('comment')">{{ myForm.error('comment') }}</div>
</div>
</div>
</template>
<script>
import { Inertia } from '#inertiajs/inertia'
export default {
components: {
Inertia
},
props: {
form: Object,
errors: Object,
},
data() {
return {
myForm: Inertia.form(
{
name: this.form.name,
comment: this.form.comment,
},
}
},
methods: {
submit() {
this.myForm.post(this.route("post.store"));
},
},
};
</script>

Related

It's possible to pass an object which has some methods to the child component in the Vue 3 JS

Parent component has the method "startMethods" which just also has some other method "onDecrementStart ". OnDecrementStart method just only call Alert.
Under this line, added a code example but that code didn't work.
<template>
<div class="parent">
<div class="main">
<img alt="Vue logo" src="../assets/logo.png">
<SettingsBoard :max="maxValue" :start="startValue"
:startInc="startMethods"
/>
</div>
</div>
</template>
<script>
import SettingsBoard from "#/components/SettingsBoard";
export default {
name: "Main",
components: {
SettingsBoard
},
methods: {
startMethods: {
onDecrementStart () {
alert('Decrement')
},
onIncrementStart () {
alert('Incriment')
}
},
}
}
</script>
SettingsBoard component
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
I want to get like that if it's possible.
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
<button :#click="startInc.onIncrementtStart()">+</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
To run a method in the parent component you should emit a custom event which has the parent method as handler :
<label>
<button #click="$emit('decrement')">-</button>
<button #click="$emit('increment')">+</button>
</label>
in parent component :
<div>
<SettingsBoard
#decrement="onDecrementStart"
#increment="onIncrementStart"
/>
...
methods: {
onDecrementStart() {
this.count--;
},
onIncrementStart() {
this.count++;
},
},
LIVE EXAMPLE

Vuejs passing function from parent to child

I have a beginner question about passing function from parent to child. In my example, I want to to use the child more times and sometimes it should to do someting else v-on:focus. How can i do that? There are options to pass it with prop but i don't know how and i think it's not good to do it ? Maybe with EventBus and if yes then how ? I want to know the right way how to do it in VueJs.
Here is the Parent Component:
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
template: `
<div>
<child></child>
<child></child>
<child></child>
</div>
`
};
And here is the child Component:
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
methods: {
functionFromChild() {
//run the function from parent
}
}
};
You can pass the function as any other prop
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
methods: {
calledFromChild(id){
console.log(id)
}
},
template: `
<div>
<child :callback="calledFromChild" id="1"></child>
<child :callback="calledFromChild" id="2"></child>
<child :callback="calledFromChild" id="3"></child>
</div>
`
};
And then in the child
export default {
name: "test",
props: ["callback", "id"],
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="() => this.calledFromChild(this.id)">
</div>
</div>
`,
}
I'm also adding an id to the child so you know which child is making the call.
But this is not a good idea. You should use emit from your child to send an event, and listen to it from the parent.
In the child
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="handleFocus">
</div>
</div>
`,
methods: {
handleFocus() {
this.$emit('focusEvent')
}
}
};
And in the parent
<child #focusEvent="handleFocusFromChild"></child>
A working example here
This should work:
const Child = {
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
props: {
functionFromParent: Function
},
methods: {
functionFromChild: function() {
this.functionFromParent();
}
},
data() {
return {
message: 'Oh hai from the component'
}
}
}
const App = {
template: `
<div>
<h1>Quick test</h1>
<p>{{ message }}</p>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
</div>
`,
components: {Child},
methods: {
functionOnParent: function(){
console.log("there we go");
}
},
data() {
return {
message: 'Hello'
}
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
If you're trying to call a function in the parent from the child component, then try
this.$parent.parentMethod()
This will invoke the method in parent component.

VueJS + VueRouter + Bootstrap-vue (modal from modal) = <failed to convert exception to string>

I'll try display modal from modal (I'll create separate components for each modal). When second modal is closed I want to return to first modal. But after I'm display first modal second time I got error 'failed to convert exception to string' from administration.js. I'm using VueRouter too.
administration.js (main entry with router-view) -> partner.vue (with partnerEditor.vue component)
partnerEditor.vue
<template>
<div>
<b-modal ref="partnerEditor" title="Partner" :busy="isBusy" cancel-title="cancel" ok-variant="info" ok-title="save" v-on:ok="onCreate">
<div class="form-group">
<div class="input-group mb-3">
<select class="form-control">
<option v-for="contact in contacts">{{ contact.name }}</option>
</select>
<div class="input-group-append">
<b-btn v-on:click="$refs.contactEditor.show()"></b-btn>
</div>
</div>
</div>
</b-modal>
<contactEditor ref="contactEditor" v-on:hide="$refs.partnerEditor.show()"></contactEditor>
</div>
</template>
<script>
export default {
data() {
return {
contacts: [],
isBusy: false
}
},
methods: {
show() {
this.$refs.partnerEditor.show();
}
},
components: {
contactEditor: () => import('./contactEditor.vue'),
},
}
</script>
contactEditor.vue
<template>
<b-modal ref="contactEditor" title="Contact" :busy="isBusy" cancel-title="cancel" ok-variant="info" ok-title="save" v-on:ok="onCreate" v-on:hide="$emit('hide')">
<div class="form-group">
<label>name</label>
<input type="text" class="form-control" placeholder="" />
</div>
</b-modal>
</template>
<style></style>
<script>
export default {
model: {
event: 'hide'
},
data() {
return {
isBusy: false
}
},
methods: {
show() {
this.$refs.contactEditor.show();
},
}
}
</script>
routes
const router = new VueRouter({
routes: [
{
name: 'configuration', path: '/configuration', component: () => import('./administrator/configuration/index.vue'),
children: [
{ name: 'configuration-partner', path: '/configuration/partner', component: () => import('./administrator/configuration/partner.vue') }
]
}
]
});

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>

Access infromation of grandparent component from grandchild component in vue.js

I have a parent component in Vue called RecipeView, and it is an inline-component. inside it, i have these components:
comments. and inside comments, i have comment and NewCommentForm, has it shows in the picture below.
I am passing in the RecipeView component the id as a prop, and would like to access it in the NewCommentForm component in order to set an endpoint that i will post to and save the comment.
This is the RecipeView component:
<recipe-view :id="{{$recipe->id}}">
<comments :data="{{$recipe->comment}}"#added="commentsCount++"></comments>
</recipe-view>
and the script for it is this:
<script>
import Comments from '../components/Comments.vue';
export default {
props: ['initialCommentsCount','id'],
components: {Comments},
data(){
return {
commentsCount: this.initialCommentsCount,
recipe_id:this.id
};
}
}
</script>
The comments component looks like this:
<template>
<div>
<div v-for="comment in items">
<comment :data="comment"></comment>
</div>
<new-comment-form :endpoint="'/comments/**Here should go the id from parent RecipeView component**'" #created="add"></new-comment-form>
</div>
</template>
<script>
import Comment from './Comment.vue';
import NewCommentForm from './NewCommentForm.vue';
export default {
props: ['data'],
components: {Comment, NewCommentForm},
data() {
return {
items: this.data,
endpoint: ''
}
},
methods: {
add(comment) {
this.items.push(comment);
this.$emit('added');
}
}
}
</script>
and this is the NewCommentForm component:
<template>
<div>
<div class="field">
<p class="control">
<input class="input"
type = "text"
name="name"
placeholder="What is your name?"
required
v-model="name">
</p>
</div>
<div class="field">
<p class="control">
<textarea class="textarea"
name="body"
placeholder="Have your say here..."
required
v-model="body">
</textarea>
</p>
</div>
<button type="submit"
#click="addComment"
class="button is-medium is-success">send</button>
</div>
</template>
<script>
export default {
props:['endpoint'],
data(){
return {
body:'',
name:'',
}
},
methods:{
addComment(){
axios.post(this.endpoint, {
body:this.body,
name: this.name
}).then(({data}) => {
this.body = '';
this.name = '';
this.$emit('created', data);
});
}
}
}
</script>
Thanks for the help.