How to add and item to an Array using Vue JS? - laravel-5

I'm using VUE JS and for some reason I don't get the new item displayed on the view. if I use the push method, nothing happens. If I use this.$set('productos',producto) the item is displayed, but when I tried to push another item to the array, the previous item is deleted.
new Vue({
el: '#ticket',
data:{
newCodigo:{
codigo: '',
},
productos:[],
},
computed:{
errors: function(){
for(var key in this.newCodigo){
if(! this.newCodigo[key])
return true;
}
return false;
}
},
methods:{
addProducto: function(){
var codigo = this.newCodigo;
this.$http.post('/api/addProducto', codigo).success(function(producto){
this.productos.push(producto);
//this.$set('productos',producto);
});
}
}
});
I'm able to retrieve the data from the DB, but it's not displayed on the view.
<div class="container">
<div class="row">
<div id="ticket">
<form action="POST" v-on:submit.prevent="addProducto">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}" >
<div class="form-group">
<label for="Codigo">
Escanea:
<span class="error" v-if="! newCodigo.codigo">*</span>
</label>
<input type="text" name="codigo" id="codigo" class="form-control" v-model="newCodigo.codigo">
</div>
</form>
<article v-for="producto in productos" :data="productos">
<h3>#{{producto.descripcion}}</h3>
<h3>#{{producto.precio}}</h3>
</article>
</div>
</div>

In the callback function of your ajax call, this does not point to the vm instance. Just change it to:
this.$http.post('/api/addProducto', codigo).success(function (producto) {
this.productos.push(producto);
}.bind(this));

Related

Is there is a way to keep my modal open in Laravel after submit and save the values to db?

I am uploading a file in Laravel 8 with having one bootstrap modal which works dynamically . everything is working fine but I want to improve my output more:
1) Update one of my forms through a modal without refreshing the page?
2) keep the modal open if the validation fails and print the errors to modal instead of my redirect page?
I will appreciate your time helping me.
my form for updating the file
<form action="{{ route('storefile' , $requisition->id) }}" method="POST"
enctype="multipart/form-data">
#csrf
#method('PUT')
<div class="form-group row">
<div class="col-sm-12">
<label for="title"> Account Status: </label>
<select class="form-control" name="acc_status">
<option value="0" {{ $requisition->acc_status == 0 ? 'selected' : '' }}> Inactive
</option>
<option value="1" {{ $requisition->acc_status == 1 ? 'selected' : '' }}> Active
</option>
</select>
</div>
<div class="col-sm-12 pt-4">
<label for="title"> Account document File: </label>
<div>
#if (!empty($requisition->acc_document))
<label class="badge-success">
{{ $requisition->acc_document }}
</label>
#else
<label class="badge-danger">
Nothing uploaded </label>
#endif
</div>
<input type="file" name="acc_document" class="form-control" id="acc_document" />
</div>
</div>
<div class="card-footer">
<div class="row">
<div class="col-md-6 text-left">
<input type="submit" value="Upload document" class="btn btn-primary">
</div>
</div>
</div>
</div>
</form>
my controller and route
public function uploadFile($id) {
$requisition = Requisition::find($id);
return view('requisition.createFile' , compact('requisition'));
}
public function storeFile(Request $request , $id) {
$request->validate([
'acc_status' => 'required',
'acc_document' => 'required|mimes:doc,docx,pdf,txt,zip|max:2000',
]);
$requisition = Requisition::find($id);
$requisition->acc_status = $request->get('acc_status');
$FileName = uniqid() .$request->file('acc_document')->getClientOriginalName();
$path = $request->file('acc_document')->storeAs('uploads', $FileName , 'public');
$requisition->acc_document = '/storage/' . $path;
}
$requisition->save();
//$requisition->update($request->all());
return back()
->with('success', 'Your file has been uploaded successfully.');
}
Route::get('upload/{id}', [RequisitionController::class, 'uploadFile'])->name('upload');
Route::put('requisition/{id}/files', [RequisitionController::class, 'storeFile'])->name('storefile');
and last part my modal and ajax in my index page to upload the file and popup will open
<div class="col-md-6">
<a style="display:inline-block; text-decoration:none; margin-right:10px;"
class="text-secondary" data-toggle="modal" id="mediumButton"
data-target="#mediumModal" title="upload"
data-attr="{{ route('upload' , $requisition->id) }}">
<i class="fas fa-upload"></i>
</a>
</div>
<script>
// display a modal (medium modal)
$(document).on('click', '#mediumButton', function(event) {
event.preventDefault();
let href = $(this).attr('data-attr');
$.ajax({
url: href,
beforeSend: function() {
$('#loader').show();
},
// return the result
success: function(result) {
// #if (count($errors) > 0) #endif
$('#mediumModal').modal("show");
$('#mediumBody').html(result).show();
$("#date-picker").datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd'
});
} ,
complete: function() {
$('#loader').hide();
},
error: function(jqXHR, testStatus, error) {
console.log(error);
alert("Page " + href + " cannot open. Error:" + error);
$('#loader').hide();
},
timeout: 8000
});
});
</script>
<!-- medium modal -->
<div class="modal fade" id="mediumModal" tabindex="-1" role="dialog" aria-labelledby="mediumModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-body" id="mediumBody">
<form id="modal-form" method="get">
<div>
<!-- the result of displayed apply here -->
</div>
</form>
</div>
</div>
</div>
</div>
#endsection
what I want to implement looks like enter image description here
There are several ways to do this kind of thing. One way to do it, is to do something like this:
Html:
<div class="modal" id="the-modal">
<form action="{{ $theAction }}" method="POST" id="the-form">
<input type="text" name="input-name" id="the-input">
<button type="submit">
</form>
<p id="the-text"></p>
</div>
In your Controller you will return an error or an 200 response, if everything is ok.
public function theAction(Request $request , $id) {
//Do stuff
if (!$error) {
return response("OK"); //This will return a 200 response
} else {
return response("An error happened", 500); //This will return a 500 error
}
}
Then, in your JS, you'll intercept form submission and then, you're going to be able to separate errors from ok messages:
<script>
$("#the-form").on('submit', function(event) {
event.preventDefault();
let theInput = $("#the-input");
$.ajax({
url: theUrl,
data: {
the-input: theInput
}
// 200 response
success: function(result) {
$("#the-text").empty();
$("#the-text").append(result.repsonse);
} ,
error: function(jqXHR, testStatus, error) {
$("#the-text").empty();
$("#the-text").append(error.response);
}
});
});
</script>
If you want to do all this stuff inside a modal, the concept is just the same: You intercept form submission, send to controller, return separate responses for errors and 200 responses, and then update manually the inputs/texts.
I did't test the code, but the concept should work.

Laravel Vuejs form builder

In my project i have some events, each with a number of tags.
These tags are defined by the administrator user.
Some of these tags may have parameters.
For example, an email tag has two "Sender" and "Receiver" parameters.
Or the transfer tag has 2 parameters "From" and "To". and etc.
Do I have to use the form builder?
How do I implement this using Laravel and Vuejs?
Use Vue JS Component in Laravel like this.
Create Contact.vue component inside resources\assets\js\components then place your Vuejs there.
<template>
<div>
<h1>Contacts</h1>
<form action="#" #submit.prevent="createContact()">
<div class="form-group">
<label>Name</label>
<input v-model="contact.name" type="text" name="name" class="form-control">
</div>
<div class="form-group">
<label>Email</label>
<input v-model="contact.email" type="text" name="email" class="form-control">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">New Contact</button>
</div>
</form>
</div>
</template>
<script>
export default {
data: function(){
return {
contact:{
name:'',
email:'',
}
}
},
methods: {
createContact: function(){
//call axios to submit the form values in your Laravel controller method
let self = this
axios.post('/contact/store', params)
.then(function(){
self.contact.name = '';
self.contact.email = '';
})
.catch(function(error){
console.log(error);
});
console.log(this.contact);
return;
}
}
}
</script>
In app.js file inside \resources\assets\js add the component
Vue.component('contacts', require('./components/Contacts.vue'));
Finally, call the contacts component inside your blade file.
<div class="container">
<div id="app">
<contacts></contacts>
</div>
</div>

Laravel & VueJS: Calling a VueJS Method Inside Blade

I'm new to Laravel & VueJS so sorry in advance for the messed up code.
I've been trying to make a registration form with VueJS being integrated into laravel to make it more dynamic.
Now I've been trying to use the # infront of the {{ }} to show laravel i'm trying to integrate VueJS but instead of VueJS responding to this it just plainly prints : {{ error }} or {{ checked ? "yes" : "no" }}.
Blade
#extends('layout.layout')
#section('content')
<section class="page-content">
<div class="container">
<article class="fillout-form">
<form action="{{ route('account.register.new') }}" method="post" id="registerForm">
{{ csrf_field() }}
#{{ error }}
<section v-if="step === 1">
<h1>Step One</h1>
<p>
<legend for="name">Your Name:</legend>
<input id="name" name="name" v-model="registration.name">
</p>
<p>
<legend for="surname">Your Lastname:</legend>
<input id="surname" name="surname" v-model="registration.surname">
</p>
<p>
<legend for="email">Your Email:</legend>
<input id="email" name="email" type="email" v-model="registration.email">
</p>
<p>
<legend for="number">Your Phone number:</legend>
<input id="number" name="number" type="number" v-model="registration.number">
</p>
<button #click.prevent="next()">Next</button>
</section>
<section v-if="step === 2">
<h2>Step Two</h2>
<p>
<legend for="account">Account Holder:</legend>
<input id="account" name="account" v-model="registration.account">
</p>
<p>
<legend for="iban">Your IBAN:</legend>
<input id="iban" name="iban" v-model="registration.iban">
</p>
<button #click.prevent="prev()">Previous</button>
<button #click.prevent="next()">Next</button>
</section>
<section v-if="step === 3">
<h3>Step Three</h3>
<p>
<input type="checkbox" v-model="checked">
#{{ checked ? "yes" : "no" }}
</p>
<button #click.prevent="prev()">Previous</button>
<button #click.prevent="submit()">Save</button>
</section>
</form>
</article>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="{{asset ('js/registerFlow.js')}}"></script>
#endsection
VueJS
const errors = {
empty: 'Please fill in all fields',
invalidmail: 'Your email is invalid',
};
const app = new Vue({
el:'#app',
mounted() {
window.addEventListener("keypress", function (event) {
if (event.keyCode === 13) {
event.preventDefault();
this.next();
}
})
},
data() {
return {
error: null,
step:1,
checked: false,
registration:{
name:null,
surname:null,
email:null,
number:null,
account:null,
iban:null,
},
}
},
methods: {
prev() {
this.step--;
},
next() {
this.error = "";
if(this.step === 1)
{
if(!this.registration.name || !this.registration.surname || !this.registration.email || !this.registration.number)
{
this.error = errors.empty;
return false;
}
}
if(this.step === 2)
{
if(!this.registration.account || !this.registration.iban)
{
this.error = errors.empty;
return false;
}
}
this.step++;
},
submit() {
alert('Submit to blah and show blah and etc.');
},
}
});
This is not the right way to use Vue.js and Laravel. Usually instead of writing everything in your blade you simply just create a component in your .blade file and move all the logic in your .vue file. I would suggest you read this to get an idea how to setup a simple form in Laravel/Vue
The Problem is, that you mix two templating engines. You could create a Vue component, thats not touched by blade and hand in your error data to be rendered.

Getting a slug to save in vue

I'm using both vue and laravel and I'm trying to create a product form that creates a slug as you type. I've managed to get
the slug to show up as you type but I'm not sure as to how I should save it to my database.
My vue
<template>
<div class="product-form">
<form v-on:submit="onSubmit(model)">
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" class="form-control" name="title" v-model="model.title">
<input type="hidden" name="slug" title="slug" :value="slug(model.title)">
<p>{{ slug(model.title) }}</p>
</div>
<input type="submit" class="btn btn-default" value="Save"></input>
</form>
</div>
</template>
<script>
export default {
data() {
return {
model: {
title: '',
slug: '',
},
};
},
methods: {
slug() {
var title = this.model.title;
var slug_test = title.replace(/\s+/g, '-');
return slug_test;
},
onSubmit: function(model){
event.preventDefault();
this.$http.post('/database/articles', model)
}
},
}
</script>
It suffices to keep the slug property of your model up to date with the title property.
The cleanest approach here would be a watcher on your title that updates the slug, or by computing model.slug right before you POST it to the server.
You can append slug in Model Resource if you are using Laravel

Laravel CSRF Field in Vue Component

I would like to ask how can I add the csrf_field() in my vue component. The error is
Property or method "csrfToken" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Here's the code:
<script>
export default {
name: 'create',
data: function(){
return {
msg: ''
}
},
props:['test']
}
</script>
<template>
<div id="app">
<form action="#" method="POST">
{{csrfToken()}}
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control">
</div>
<div class="form-group">
<label for="location">Location</label>
<input type="text" id="location" class="form-control">
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" id="age" class="form-control">
</div>
<div class="form-group">
<input type="submit" class="btn btn-default">
</div>
</form>
</div>
</template>
As I already wrote here, I would simply suggest to put this in your PHP file:
<script>
window.Laravel = <?php echo json_encode(['csrfToken' => csrf_token()]); ?>
</script>
This way you're able to easily import your csrfToken from the JS part (Vue in this case).
Moreover, if you insert this code in your PHP layout file, you can use the token by any component of your app, since window is a JS global variable.
Source: I got the trick from this post.
Laravel version 5 or later
First you need to store your CSRF token in a HTML meta tag in your header:
<meta name="csrf-token" content="{{ csrf_token() }}">
Then you can add it to your script:
<script>
export default {
name: 'create',
data: function(){
return {
msg: '',
csrf: document.head.querySelector('meta[name="csrf-token"]').content
}
},
props:['test']
}
</script>
And in the template:
<template>
<div id="app">
<form action="#" method="POST">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control">
</div>
<div class="form-group">
<label for="location">Location</label>
<input type="text" id="location" class="form-control">
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" id="age" class="form-control">
</div>
<div class="form-group">
<input type="submit" class="btn btn-default">
</div>
<input type="hidden" name="_token" :value="csrf">
</form>
</div>
</template>
Try this:
<script>
export default {
name: 'create',
data: function(){
return {
msg: '',
csrf: window.Laravel.csrfToken
}
},
props:['test']
}
</script>
And in your markup just use
<input type="hidden" name="_token" :value="csrf" />
EDIT
Bit of a rabbit hole but, one great feature of Vue is that it can easily handle POST, PATCH, etc. requests using AJAX and the vue-resource extension. Instead of using a <form> here you can process this data using your Vue component. If you were to take this route, you can set default headers to send with each request no matter what method it is, so you can always send your CSRF token.
exports deafult{
http: {
headers: {
'X-CSRF-TOKEN': window.Laravel.csrfToken
}
}
}
if you look at the /resources/assets/js/bootstrap.js you will find these lines
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');
}
I believe you are using axios for your requests. this means you need to add
<meta name="csrf-token" content="{{csrf_token}}">
in your <head> tag.
Came across this problem whilst building multiple forms in a vue v-for loop.
added to data;
csrf: document.head.querySelector('meta[name="csrf-token"]').content,
Added hidden form element
<input type="hidden" name="_token" :value="csrf" />
I think you need to write it like this:
{{ crsf_token() }}
and not like this:
{{ csrfToken() }}
If that doesn't work, maybe try this:
{!! csrf_token !!}

Resources