Getting a vue form to go to the next page after submit in laravel - laravel

I'm using both laravel and vue and I'm trying to get my vue form to go to another page when I've submitted, but the problem is that once it's submitted it refreshes and goes back to the page it's on instead of the other page.
When I check my network tab in the dev tools, it posted to the right page but it doesn't show up in the browser
This is my vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an Supplier Code Selection component.
<br>
<form #submit.prevent="submit">
<label for="parent-product">Parent Product</label>
<select name="parent-product" id="parent-product" class="form-control" v-model="selected_parent">
<option>Please select your code</option>
<option v-for="product in products" :value="product.id">
{{ product.supplier_code }}
</option>
<option v-for="child in children" :value="child.id">
{{ child.supplier_code }}
</option>
</select>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: [
'products',
'children',
'selected_parent'
],
mounted() {
console.log('Component mounted.')
},
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData);
},
},
}
</script>
My route
Route::any('/add-to-cart/{id}', 'PublicController#getAddToCart')->name('product.addToCart');
My controller function
public function getAddToCart(Request $request, $id)
{
$menus_child = Menu::where('menu_id', 0)->with('menusP')->get();
$contacts = Contact::all();
$product = Product::find($id);
$supplier_code = $request->supplier_code;
$oldCart = Session::has('cart') ? Session::get('cart') : null;
$cart = new Cart($oldCart);
$cart->add($product, $product->id, $supplier_code);
$request->session()->put('cart', $cart);
return view('public.shopping-cart', ['products' => $cart->items, 'totalPrice' => $cart->totalPrice, 'menus_child' => $menus_child, 'contacts' => $contacts, 'supplier_code' => $supplier_code]);
}

You could just add a location if the axios call returns success. Ps. untested code.
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData)
.then(response => { location: '/'});
},

I found my issue. I forgot to change
return view('public.shopping-cart', compact('supplier_code'));
to
return ['redirect' => route('product.shoppingCart')];
in my laravel controller function

Related

422 Unprocessable Entity Error. I tried upload Excel file to import data but it is not working

This is my code, i use vuejs and laravel. The function I am doing is both saving the Course and also importing the Excel file to get the data in the file. But when saving the Course is normal, but when it comes to running the validate excel file line, the error is not received. Even though I gave it a name and console.log came out. Can you help me fix it? Thank you so much.
Component - template tag
<form #submit.prevent="store()" #keydown="form.onKeydown($event)">
<div class="form-group">
<label class="form-label">Courses</label>
<select class="form-control" name="education_program_course" v-model="form.education_program_course">
<option value="" disabled selected="">Select option</option>
<option value="" disabled>-------</option>
<option v-for="course in courses" :key="course.course_code" :value="course.course_code">{{ course.course_name }}</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Excel File</label>
<input type="file" class="form-control" id="file_data" name="file_data" ref="fileupload" #change="onFileChange">
</div>
<div class="card-footer text-right">
<button :disabled="form.busy" class="btn btn-success btn-lg mt-1" type="submit">Save</button>
</div>
</form>
Component - script tag
export default: {
data() {
return {
file_data: "",
form: new Form({
education_program_course
}),
},
methods: {
onFileChange(e) {
this.file_data = e.target.files[0];
},
store() {
this.form.busy = true;
let formData = new FormData();
formData.append('file_data', this.file_data);
this.form.post('../../api/admin/program/education', formData, {
headers: { 'content-type': 'multipart/form-data' }
})
.then(res => {
if(this.form.successful){
this.$snotify.success('Sucessful!');
this.form.clear();
this.form.reset();
}
})
.catch(err => {
this.$snotify.error(err.response.data.errors.file_data[0]);
});
},
}
}
}
Controller
$data = $request->validate([
'education_program_course' => ['required'],
'file_data' => ['required', 'file', 'mimes:xls,xlsx']
]);
$program = new EducationProgram();
$program->education_program_course = $data['education_program_course'];
$path = $request->file($data['file_data'])->getRealPath();
Excel::import(new ProgramDetailImport, $path);
You should append 'education_program_course' on your formdata.
formData.append('education_program_course', this.form.education_program_course);
I have solved this problem. Change change at script tag -> store()
from:
let formData = new FormData();
formData.append('file_data', this.file_data);
to:
if(document.getElementById("file_data").files[0]){
this.form.file_data = document.getElementById("file_data").files[0];
}
Adding file_date in form: new Form and remove formData in this.form.post.

how to access axios response in blade - laravel view

How to access axios result from vue component in blade file? I tried accessing {{value}} within 'app' div also. But the error still remains. I want to generate partial views based on the value of axios response.
IssueComponent.vue
<template>
<div>
<div class="form-group">
<label>Enter Accession No</label>
<input
type="text"
name="accession_no"
placeholder="Accession No"
class="form-control"
v-on:keyup.enter="getData()"
v-model="query"
/>
</div>
<div>
<button class="btn btn-info" #click.prevent="getData()">Check</button>
</div>
</template>
<script>
export default {
data() {
return {
query: "",
value: []
};
},
methods: {
getData: function() {
var self = this;
axios
.get("/issue-getdata", { params: { q: self.query } })
.then(function(response) {
self.value = response.data;
})
.catch(function(error) {
console.log(error);
})
.then(function() {
});
}
}
};
</script>
create.blade.php
<form action="/issue" method="POST">
<div id="app">
<issue-component></issue-component>
</div>
{{value}} ///////// Undefined constant error
<button type="submit" class="button-btn btn-success">Submit</button>
#csrf
</form>
Controller Method
public function getData(Request $request){
$input = $request->q;
$acsNo = preg_replace("/[^0-9]/", "", $input);
$acsNoIssued = Issue::where('accession_no', '=', $acsNo)->where('is_returned', null)->orwhere('is_returned', 0)->first();
return response()->json($acsNoIssued);
}
The Error
Facade\Ignition\Exceptions\ViewException
Use of undefined constant value - assumed 'value' (this will throw an Error in a future version of PHP) (View: D:\ProgrammingSSD\laragon\www\ulclibrary\resources\views\issues\create.blade.php)
You can't. Blade is rendered server side. By the time your vue component makes the request, that {{ $value }} is already parsed and is now a static part of your view.
What you could do is save the state (the information) in VUE, and read it using another VUE component that will display the info (instead of blade).
Guide for states in vue
https://vuex.vuejs.org/guide/state.html
<form action="/issue" method="POST">
<div id="app">
<issue-component></issue-component>
</div>
<display-component-value></display-component-value> // Vue component that reads the state you want
<button type="submit" class="button-btn btn-success">Submit</button>
#csrf
</form>

After Login the get route not worked in vue js Component in laravel

i want to load my provinces list in vue.js component , my code is worked , but when i login , show nothing in my list , here is my code
in my vuejs component :
<template>
<div class="row">
<div class="form-group required col-6">
<label for="province_id">Province</label>
<div class="col-12">
<select class="form-control" name="province_id[]" id="province_id" multiple v-model="province" style="height: 200px"
#change="getAllCities()">
<option v-for="province in provinces" :value="province.id">{{province.name}}</option>
</select>
</div>
</div>
<div class="form-group required col-6" v-if="cities.length> 0">
<label for="city_id">City</label>
<div class="col-12">
<select class="form-control" multiple="" name="city_id" id="city_id" style="height: 200px">
<option v-for="city in cities" :value="city.id">{{city.name}}</option>
</select>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
province: 'Select Province',
cities: [],
provinces: [],
flag: false,
}
},
mounted() {
axios.get('/api/provinces').then(res => {
// console.log(res);
this.provinces = res.data.provinces;
}).catch(err => {
// console.log (err)
})
},
methods: {
getAllCities: function () {
console.log()
axios.get('/api/cities/(' + this.province + ')').then(res => {
console.log(res);
this.cities = res.data.cities
}).catch(err => {
// console.log (err)
})
},
}
}
</script>
in Register Controller :
public function getAllProvinces()
{
$provinces=Province::all();
$response=['provinces'=>$provinces];
return response()->json($response,200);
}
my code work before login and show all provinces in my db , but after login there is nothin in my select , please help about that .
It happens because RegisterController uses guest middleware, you just don't pass the middleware when logged in, also it isn't good to me to use any method not related to the controller. Create new ProvinceController and define your method there it'll work
// this is RegisterController constructor
public function __construct()
{
$this->middleware('guest');
}
But if you somehow want to use your method exactly in the RegisterController then use except() method in constructor:
// add except
public function __construct()
{
$this->middleware('guest')->except(['getAllProvinces']);
}

Laravel search results in second page

I'm working with vuejs in laravel 5.6 and I've made search form with 2 inputs, but i'm not sure how to show results in different page and my data return by json.
Logic
User fill inputs/input -> click search button
User redirects to other page -> see results
Code
controller
public function indexsearch(Request $request)
{
$area = request('area');
$keywords = request('keywords');
if($keywords){
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$keywords}%")
->get();
}else{
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$keywords}%")
->get();
}
return response()->json($projects);
}
route
Route::get('indexsearch', 'Api\SearchController#indexsearch');
component
<template>
<div>
<div class="form-row justify-content-center align-items-center mmt-5">
<div class="col-md-4">
<label class="sr-only" for="inlineFormInput">Query</label>
<input v-model.lazy="keywords" type="text" class="customformdes form-control mb-2" id="inlineFormInput" placeholder="E.g. Larave, Vue, Design, Writer">
</div>
<div class="col-md-4">
<label class="sr-only" for="inlineFormInputGroup">Area</label>
<input v-model.lazy="area" type="text" class="customformdes form-control mb-2" id="inlineFormInputGroup" placeholder="E.g. Jabodetabek, Medan, Surabaya">
</div>
<div class="col-md-2">
<button type="button" class="customformdes btn btn-block btn-primary mb-2"><i class="fab fa-searchengin"></i> Search</button>
</div>
</div>
// currently this part shows the result as hidden drop-down
<b-list-group v-if="results.length > 0">
<b-list-group-item v-for="result in results" :key="result.id" :to="`/projects/${result.slug}`">{{result.title}}</b-list-group-item>
</b-list-group>
</div>
</template>
<script>
var _ = require('lodash');
import navbar from './navbar.vue';
export default {
data() {
return {
keywords: null,
area: null,
results: []
};
},
watch: {
keywords(after, before) {
this.fetch();
}
},
methods: {
fetch() {
axios.get('/api/indexsearch', { params: { keywords: this.keywords, area: this.area } })
.then(response => this.results = response.data)
.catch(error => {});
}
}
}
</script>
Issues
By my current control code I cannot get my second input (area) alone, meaning: If I don't fill first input and just fill second input i cannot get results.
I don't know how to show results in other page and not as drop-down in the same page (part is commented in my component code)
Thanks in advance.
For your first issue, you can add more watcher for the area:
(Since you are just calling a single method from your watcher, you can just directly use the method's name as the value of your watcher as it is just the same.)
watch: {
keywords: 'fetch',
area: 'fetch'
},
you can also use $vm.watch to watch for multiple values at the same time.
Instead of:
watch: {
keywords(after, before) {
this.fetch();
}
},
you can have it like this:
mounted() {
this.$watch(
function() {
return this.keywords + this.area
},
(newVal, oldVal) => {
this.fetch();
}
)
}
As for the second issue, you can pass props data to the target page.
See this example code below:
ResultsPage.vue:
<template>
<div>
<div v-for="result in results" :key="result.id">{{ result.title }}</div>
</div>
</template>
<script>
export default {
props: ['results']
}
</script>
router configuration:
import ResultsPage from 'ResultsPage.vue'
new Router({
routes: [
// ... some other routes in here
{
path: '/results',
name: 'results',
props: true,
component: ResultsPage,
}
]
})
then you can now navigate to the ResultsPage by using $router.push():
methods: {
fetch() {
axios.get('/api/indexsearch', {
params: {
keywords: this.keywords,
area: this.area
}
})
.then(response => {
this.$router.push({
name: 'results',
params: {
results: response.data
}
})
})
.catch(error => {});
}
}
You will need to add $area as your search term to solve the first issue.
And to show the results on a separate page you will need to request a route that returns a view in your search method.
// Controller
public function indexsearch(Request $request)
{
$area = request('area');
$keywords = request('keywords');
if($keywords){
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$area}%")
->get();
}else{
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$area}%")
->get();
}
return view('search_results', compact('projects'));
}
If you want to show the results on a separate page then you don't really need vue here.
You can do something like this to submit a form in your view:
<form action="search"> <!-- or whatever your searchindex route is -->
<div class="form-row justify-content-center align-items-center mmt-5">
<div class="col-md-4">
<label class="sr-only" for="inlineFormInput">Query</label>
<input v-model.lazy="keywords" type="text" class="customformdes form-control mb-2" id="inlineFormInput" placeholder="E.g. Larave, Vue, Design, Writer">
</div>
<div class="col-md-4">
<label class="sr-only" for="inlineFormInputGroup">Area</label>
<input v-model.lazy="area" type="text" class="customformdes form-control mb-2" id="inlineFormInputGroup" placeholder="E.g. Jabodetabek, Medan, Surabaya">
</div>
<div class="col-md-2">
<button type="button" class="customformdes btn btn-block btn-primary mb-2"><i class="fab fa-searchengin"></i> Search</button>
</div>
</div>
</form>
Then in your search results blade (search_results.blade.php) view:
<div>
<b-list-group">
#foreach($projects as $project)
<b-list-group-item>{{ $project->title }}</b-list-group-item>
#endforeach
</b-list-group>
</div>
I hope this helps solve your issue.

No error messages from 422 response on laravel form request from vue component

I'm trying to submit a form request using axios, have it validated, and return errors if the validation fails. The problem is, when I submit the form, no error messages are returned for me to show on the client side. Here's the HTTP request and vue component:
<div class="card">
<div class="card-header">
<h4>Information</h4>
</div>
<div class="card-body">
<p v-if='!isEditMode'><strong>Name:</strong> {{businessData.name}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-name">Name</label></strong>
<input class="form-control" name="business-name" v-model='businessData.name'>
</div>
<p v-if='!isEditMode'><strong>Description:</strong> {{businessData.description}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-description">Description</label></strong>
<textarea class="form-control normal" name="business-description"
placeholder="Enter your services, what you sell, and why your business is awesome"
v-model='businessData.description'></textarea>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Address Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Street Address: </strong> {{businessData.street}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-street">Street Address: </label></strong>
<input type="text" class="form-control" name="business-street" v-model='businessData.street' placeholder="1404 e. Local Food Ave">
</div>
<p v-if="!isEditMode"><strong>City: </strong> {{businessData.city}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-city">City: </label></strong>
<input class="form-control" type="text" name="business-city" v-model='businessData.city'>
</div>
<p v-if="!isEditMode"><strong>State: </strong> {{businessData.state}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-state">State: </label></strong>
<select class="form-control" name="business-state" id="state" v-model="businessData.state" >...</select>
</div>
<p v-if="!isEditMode"><strong>Zip: </strong> {{businessData.zip}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-zip">Zip: </label></strong>
<input class="form-control" type="text" maxlength="5" name="business-zip" v-model='businessData.zip'>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Contact Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Phone: </strong> {{businessData.phone}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-phone">Phone: </label></strong>
<input class="form-control" type="tel" name="business-phone" v-model='businessData.phone'>
</div>
<p v-if="!isEditMode"><strong>Email: </strong> {{businessData.email}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-Email">Email: </label></strong>
<input class="form-control" type="email" name="business-email" v-model='businessData.email'>
</div>
</div>
</div>
</div>
<script>
export default {
data () {
return {
isEditMode: false,
businessData: this.business,
userData: this.user,
errors: []
}
},
props: {
business: {},
user: {},
role: {}
},
//Todo - Institute client side validation that prevents submission of faulty data
methods: {
validateData(data) {
},
saveBusinessEdits () {
axios.put('/businesses/' + this.business.id , {updates: this.businessData})
.then(response => {
console.log(response.data)
// this.businessData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response.data)
this.isEditMode = false;
})
},
saveUserEdits () {
axios.put('/profile/' + this.user.id , {updates: this.userData})
.then(response => {
console.log(response.data)
this.userData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response)
this.isEditMode = false;
})
}
}
}
Route
Route::put('/businesses/{id}', 'BusinessesController#update');
BusinessController and update function
public function update(BusinessRequest $request, $id)
{
$business = Business::find($id)->update($request->updates);
$coordinates = GoogleMaps::geocodeAddress($business->street,$business->city,$business->state,$business->zip);
if ($coordinates['lat']) {
$business['latitude'] = $coordinates['lat'];
$business['longitude'] = $coordinates['lng'];
$business->save();
return response()->json($business,200);
} else {
return response()->json('invalid_address',406);
}
$business->save();
return response()->json($business,200);
}
and BusinessRequest class
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric',
];
}
public function messages() {
return [
'business-zip.min:5' =>'your zip code must be a 5 characters long',
'business-email.email'=>'your email is invalid',
'business-phone.numeric'=>'your phone number is invalid',
];
}
}
I don't understand why, even if input valid data, it responds with a 422 response and absolutely no error messages. Since this is laravel 5.6, the 'web' middleware is automatic in all of the routes in the web.php file. So this isn't the problem. Could anyone help me normalize the validation behavior?
In Laravel a 422 status code means that the form validation has failed.
With axios, the objects that are passed to the then and catch methods are actually different. To see the response of the error you would actually need to have something like:
.catch (error => {
console.log(error.response)
this.isEditMode = false;
})
And then to get the errors (depending on your Laravel version) you would have something like:
console.log(error.response.data.errors)
Going forward it might be worth having a look at Spatie's form-backend-validation package
You can use Vue.js and axios to validate and display the errors. Have a route called /validate-data in a controller to validate the data.
app.js file:
import Vue from 'vue'
window.Vue = require('vue');
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
class Errors {
constructor() {
this.errors = {};
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
record(errors) {
this.errors = errors;
}
clear(field) {
delete this.errors[field];
}
has(field) {
return this.errors.hasOwnProperty(field);
}
any() {
return Object.keys(this.errors).length > 0;
}
}
new Vue({
el: '#app',
data:{
errors: new Errors(),
model: {
business-name: '',
business-description: '',
business-phone: ''
},
},
methods: {
onComplete: function(){
axios.post('/validate-data', this.$data.model)
// .then(this.onSuccess)
.catch(error => this.errors.record(error.response.data.errors));
},
}
});
Make a route called /validate-data with a method in the controller, do a standard validate
$this->validate(request(), [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric'
]
);
Then create your inputs in your view file, using v-model that corresponds to the vue.js data model fields. Underneath it, add a span with an error class (basic red error styling, for example) that only shows up if the errors exist. For example:
<input type="text" name="business-name" v-model="model.business-name" class="input">
<span class="error-text" v-if="errors.has('business-name')" v-text="errors.get('business-name')"></span>
Don't forget to include the app.js file in footer of your view file. Remember to include the tag, and run npm run watch to compile the vue code. This will allow you to validate all errors underneath their input fields.
Forgot to add, have a buttton that has #onclick="onComplete" to run the validate method.

Resources