Getting dynamic field data using vue.js in laravel - laravel

I am creating multiple dynamic fields and the same is posted to the laravel controller methods but i'm able to get the simple fields data (fields that are not dynamically generated) but the array fields that are dynamically generated those values i'm unable to get in the controller method.
<input :name="'students['+studentdetails.id+']['+studentdetails.class+']'" type="text" class="form-control"/>
<input :name="'students['+studentdetails.id+']['+studentdetails.class+']'" type="text" class="form-control"/>
In my controller method when i'm getting it:
printr($request->attributes);
export default {
data() {
...
return {
form: new Form({
title: "",
attributes: [],
}),
};
addStudent() {
const header = {
Authorization: "Bearer " + this.token,
};
this.form
.post(APP_URL + `/api/addStudent`, { headers: header })
.then((response) => {
if (response.status == 200) {
location.replace(APP_URL + "/success");
}
});
},
};
there is not value.

You are now creating [] and students as strings, and they should not be. Remove the string quotes completely, as you are using variables only:
:name="students[studentdetails.id][studentdetails.class]"

Related

Is FormData object required with Vuejs to upload file to laravel backend?

Is the FormData object absolutely necessary to work with files and to upload them to my backend made with laravel?
Secondly, would it be a good practice to wrap all form inputs in a FormData object, even non file input?
Currently I try this
<input type="file" #change="onVideoSelected" name="explenationVideo">
My method logic:
onVideoSelected(event) {
this.form.video = event.target.files[0];
}
My data structure
data() {
return {
form: {
name : '',
contentType : 'video',
imageGroup : [],
video : null,
difficulty : '',
}
}
}
And when I submit
axios.post('/backoffice/exercises', this.form)
.then(({data}) => {
this.messages.push('Exercice ajouté!');
});
But when I try to view the detail in the backend like
dd($request->file('video'));
I have a null value.

Laravel vue show old data on update fields

So I've made update function for my component and it's working perfectly the only issue is I cannot show old data (if there is any) to the user,
This is what I have now:
As you see not only i can send my form data to back-end for update, but also I have the saved data already.
Code
export default {
data: function () {
return {
info: '', //getting data from database
profile: { //sending new data to back-end
photo: '',
about: '',
website: '',
phone: '',
state: '',
city: '',
user_id: '',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
}
}
},
mounted: function() {
this.isLoggedIn = localStorage.getItem('testApp.jwt') != null;
this.getInfo();
},
beforeMount(){
if (localStorage.getItem('testApp.jwt') != null) {
this.user = JSON.parse(localStorage.getItem('testApp.user'))
axios.defaults.headers.common['Content-Type'] = 'application/json'
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('testApp.jwt');
console.log()
}
},
methods: {
update() { // sending data to back-end
let user_id = this.user.id;
let photo = this.profile.photo;
let about = this.profile.about;
let website = this.profile.website;
let phone = this.profile.phone;
let state = this.profile.state;
let city = this.profile.city;
axios.put('/api/updateprofile/'+ user_id, {user_id, photo, about, website, phone, state, city}).then((response) => {
this.$router.push('/profile');
$(".msg").append('<div class="alert alert-success" role="alert">Your profile updated successfully.</div>').delay(1000).fadeOut(2000);
});
Vue.nextTick(function () {
$('[data-toggle="tooltip"]').tooltip();
})
},
getInfo: function() { //getting current data from database
let user_id = this.user.id;
axios.get('/api/show/'+ user_id).then((response) => {
this.info = response.data;
console.log(response);
});
},
}
}
Component sample field
// this shows my about column from database
{{info.about}}
// this sends new data to replace about column
<textarea name="about" id="about" cols="30" rows="10" class="form-control" v-model="profile.about" placeholder="Tentang saya..."></textarea>
Question
How to pass old data to my fields (sample above)?
Update
Please open image in big size.
This can be done by assigning this.profile the value of this.info on your Ajax response.
This way you will have input fields set up with original values.
function callMe() {
var vm = new Vue({
el: '#root',
data: {
profile:{},
info:{}
},
methods: {
getInfo: function() { //getting current data from database
this.info={about:"old data"}//your response here
this.profile=Object.assign({},this.info);
},
},
})
}
callMe();
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.11/dist/vue.js"></script>
<div id='root'>
<button #click="getInfo">Ajax Call click me</button>
Input <input v-model="profile.about"/>
<p>profile:{{this.profile}}</p>
<p>info: {{this.info}}</p>
</div>
The problem with the code is that after assigning new value info is not reactive anymore. You need to keep "info" like this in the start.
info: { // data from api
photo: '',
about: '',
website: '',
phone: '',
state: '',
city: '',
user_id: '',
}
And after fetching values from api update each value separately.
getInfo: function() { //getting current data from database
let user_id = this.user.id;
axios.get('/api/show/'+ user_id).then((response) => {
this.info.photo = response.data.photo;
this.info.about = response.data.about;
//all other values
console.log(response);
});
},
In your textarea you have a model profile.about, the way to show the "old data", is to assing to that model the data
in the create or mounted method you have to assing like
this.profile.about = this.info.about
this way profile.about will have the data stored in your db, that way if the user update it, the old data will be keep safe in this.info.about and the edited in this.profile.about

Vue.js Component combined with Laravel Form Validation (i.e. passing an initial value for data)

After much Googling and finding the Vue.js forum down, I am ready to give up.
I'm creating a Postcode Lookup component, and everything was working well until I tried to combine it with Laravel's form validation - particularly when there's an error, and the form re-fills the old values.
Hopefully I cover everything here. I have a form input partial that I use which generates every form input. It also uses Laravel's old(...) value if present.
The issue is because there's a default value (in this case for postcode and address) of an empty string, this overrides the value attribute of Postcode input, and the content of the Address textarea.
In made up land, the ideal would be:
data : function() {
return {
postcode : old('postcode'),
address : old('address'),
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
So that's what I'm trying to replicate.
I can probably replace validation with Ajax validation, but my form partial changes the appearance of fields with an error slightly, so this would be messy
From my understanding:
I can't set an initial data value, as this will override the input value.
I can set a prop, but this is immutable
Any help I can find suggests 'using a computed property which determines its value from the prop' but if you literally do that, it doesn't update.
Here's what I have so far:
<so-postcode-lookup initial-postcode="{{ old('postcode') }}" initial-address="{{ old('address') }}"></so-postcode-lookup>
/**
* Allow user to select an address from those found in the postcode database
*/
Vue.component('so-postcode-lookup', {
name : 'so-postcode-lookup',
template : '#so-postcode-lookup-template',
props : ['initialPostcode', 'initialAddress'],
data : function() {
return {
postcode : '',
address : '',
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
computed : {
currentAddress : function() {
if (this.address !== '') {
return this.address;
} else {
return this.initialAddress;
}
},
currentPostcode : function() {
if (this.postcode !== '') {
return this.postcode;
} else {
return this.initialPostcode;
}
},
hasAddresses : function() {
return this.addresses.length;
},
isValidPostcode : function() {
return this.postcode !== '' && this.postcode.length > 4;
},
isInvalidPostcode : function() {
return !this.isValidPostcode;
}
},
methods : {
fetchAddresses : function() {
var resource = this.$resource(lang.ajax.apiPath + '/postcode-lookup{/postcode}');
var $vm = this;
var element = event.currentTarget;
// Fetch addresses from API
resource.get({ postcode : this.postcode }).then(function(response) {
response = response.body;
if (response.status == 'success') {
// Update addresses property, allowing select to be displayed
$vm.addresses = response.data;
} else {
$vm.addresses = [];
}
this.hasResponse = true;
});
},
setAddress : function() {
this.address = this.selectedAddress;
}
}
});
<template id="so-postcode-lookup-template">
<div class="row">
#include('partials.input', [
'label' => trans('register.form.postcode'),
'sub_type' => 'postcode',
'input_id' => 'postcode',
'autocorrect' => false,
'input_attributes' => 'v-model="currentPostcode"',
'suffix_button' => true,
'suffix_button_reactive' => trans('register.form.postcode_button_reactive'),
'suffix_text' => trans('register.form.postcode_button'),
'required' => true,
'columns' => 'col-med-50',
'wrapper' => 'postcode-wrapper'
])
<div class="col-med-50 form__item" v-show="hasResponse">
<label for="address-selector" class="form__label" v-show="hasAddresses">{{ trans('forms.select_address') }}</label>
<select id="address-selector" class="form__select" v-show="hasAddresses" v-model="selectedAddress" #change="setAddress">
<template v-for="address in addresses">
<option :value="address.value">#{{ address.text }}</option>
</template>
</select>
<so-alert type="error" allow-close="false" v-show="!hasAddresses">{{ trans('forms.no_addresses') }}</so-alert>
</div>
#include('partials.input', [
'label' => trans('register.form.address'),
'input_id' => 'address',
'type' => 'textarea',
'input_attributes' => 'v-model="currentAddress"',
'required' => true
])
</div>
</template>
If I try this, and set the model of the inputs to currentPostcode and currentAddress respectively, I seem to get an infinite loop.
I think I'm overthinking this somehow.
You can't bind directly to a prop but you can set an initial value using the prop and then bind to that, which is the way to go if you need a two way binding:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
created() {
// copy postcode to data
this.postcode = this.initPostcode;
},
data() {
return {
postcode: ""
}
},
template: '<span><input type="text" v-model="postcode"> {{ postcode }}</span>'
});
Then just do:
<div id="app">
<my-input init-postcode="{{ old('postcode') }}"></my-input>
</div>
Here's the fiddle: https://jsfiddle.net/vL5nw95x/
If you are just trying to set the initial values, but don't need a two way binding, then you can reference the prop directly - as you won't be applying any changes - using v-bind:value:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
template: '<span><input type="text" :value="initPostcode"> {{ postcode }}</span>'
});
And the markup:
Here's the fiddle: https://jsfiddle.net/pfdgq724/
Im working in a easy way to do that using laravel 5.4 controller to send the data directly
In Laravel view:
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="documento.ciudad" value="{{ old('ciudad', isset($documento->ciudad) ? $documento->ciudad : null) }}" >
in vue.js 2.0
data: {
documento: {
ciudad: $('#ciudad').val(),
},
},
In Laravel Controller
$documento = ReduJornada::where("id_documento",$id)->first();
return view('documentos.redujornada')->with(compact('documento'));

Angular 2 (Beta) Server side validation messages

I am looking for an elegant way to display validation messages from a server side API without having to create custom validators or hard coding all possible messages in the UI.
I need to add error messages to specific fields as well as to the entire form.
This must work in Angular 2.0.0-beta.3
There are two kinds of server validations:
The global ones (for the whole form or corresponding to an error during the form submission)
The ones related to fields
For the one, simply extract the message from the response payload and put it into a property of your component to display it into the associated template:
#Component({
(...)
template: `
<form (submit)="onSubmit()">
(...)
<div *ngIf="errorMessage">{{errorMessage}}</div>
<button type="submit">Submit</button>
</form>
`
})
export class MyComponent {
(...)
onSubmit() {
this.http.post('http://...', data)
.map(res => res.json())
.subscribe(
(data) => {
// Success callback
},
(errorData) => {
// Error callback
var error = errorData.json();
this.error = `${error.reasonPhrase} (${error.code})`;
}
);
}
}
I assume that the response payload for error is a JSON one and corresponds to the following:
{
"code": 422,
"description": "Some description",
"reasonPhrase": "Unprocessable Entity"
}
For the second one, you can set received error message within controls associated with form inputs, as described below:
#Component({
(...)
template: `
<form [ngFormModel]="myForm" (submit)="onSubmit()">
(...)
Name: <input [ngFormControl]="myForm.controls.name"/>
<span *ngIf="myForm.controls.name.errors?.remote"></span>
(...)
<button type="submit">Submit</button>
</form>
`
})
export class MyComponent {
(...)
constructor(builder:FormBuilder) {
this.myForm = this.companyForm = builder.group({
name: ['', Validators.required ]
});
}
onSubmit() {
this.http.post('http://...', data)
.map(res => res.json())
.subscribe(
(data) => {
// Success callback
},
(errorData) => {
// Error callback
var error = errorData.json();
var messages = error.messages;
messages.forEach((message) => {
this.companyForm.controls[message.property].setErrors({
remote: message.message });
});
}
);
}
}
I assume that the response payload for error is a JSON one and corresponds to the following:
{
messages: [
{
"property": "name",
"message": "The value can't be empty"
]
}
For more details you can have a look at this project:
https://github.com/restlet/restlet-sample-angular2-forms/blob/master/src/app/components/company.details.ts
https://github.com/restlet/restlet-sample-angular2-forms/blob/master/src/app/components/form.field.ts
I present you the definitive displayErrors function (Handles server side validations following the JSONAPI Standard):
You will need Underscore.js
displayErrors(error: ErrorResponse) {
let controls = this.supportRequestForm.controls;
let grouped = _.groupBy(error['errors'], function(e) {
return e['source']['pointer'];
});
_.each(grouped, function(value, key, object) {
let attribute = key.split('/').pop();
let details = _.map(value, function(item) { return item['detail']; });
controls[attribute].setErrors({ remote: details.join(', ') });
});
}

Vue.js Retrieving Remote Data for Options in Select2

I'm working on a project that is using Vue.js and Vue Router as the frontend javascript framework that will need to use a select box of users many places throughout the app. I would like to use select2 for the select box. To try to make my code the cleanest I can, I've implemented a custom filter to format the data the way select2 will accept it, and then I've implemented a custom directive similar to the one found on the Vue.js website.
When the app starts up, it queries the api for the list of users and then stores the list for later use. I can then reference the users list throughout the rest of the application and from any route without querying the backend again. I can successfully retrieve the list of users, pass it through the user list filter to format it the way that select2 wants, and then create a select2 with the list of users set as the options.
But this works only if the route that has the select2 is not the first page to load with the app. For example, if I got to the Home page (without any select2 list of users) and then go to the Users page (with a select2), it works great. But if I go directly to the Users page, the select2 will not have any options. I imagine this is because as Vue is loading up, it sends a GET request back to the server for the list of users and before it gets a response back, it will continues with its async execution and creates the select2 without any options, but then once the list of users comes back from the server, Vue doesn't know how to update the select2 with the list of options.
Here is my question: How can I retrieve the options from an AJAX call (which should be made only once for the entire app, no matter how many times a user select box is shown) and then load them into the select2 even if the one goes directly to the page with the select2 on it?
Thank you in advance! If you notice anything else I should be doing, please tell me as I would like this code to use best practices.
Here is what I have so far:
Simplified app.js
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
globals: {
users: {
data: []
},
}
};
},
methods: {
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
});
},
}
});
Sample response from API
{
"data": [
{
"id": 1,
"first_name": "John",
"last_name": "Smith",
"active": 1
},
{
"id": 2,
"first_name": "Emily",
"last_name": "Johnson",
"active": 1
}
]
}
User List Filter
Vue.filter('userList', function (users) {
if (users.length == 0) {
return [];
}
var userList = [
{
text : "Active Users",
children : [
// { id : 0, text : "Item One" }, // example
]
},
{
text : "Inactive Users",
children : []
}
];
$.each( users, function( key, user ) {
var option = { id : user.id, text : user.first_name + ' ' + user.last_name };
if (user.active == 1) {
userList[0].children.push(option);
}
else {
userList[1].children.push(option);
}
});
return userList;
});
Custom Select2 Directive (Similar to this)
Vue.directive('select', {
twoWay: true,
bind: function () {
},
update: function (value) {
var optionsData
// retrive the value of the options attribute
var optionsExpression = this.el.getAttribute('options')
if (optionsExpression) {
// if the value is present, evaluate the dynamic data
// using vm.$eval here so that it supports filters too
optionsData = this.vm.$eval(optionsExpression)
}
var self = this
var select2 = $(this.el)
.select2({
data: optionsData
})
.on('change', function () {
// sync the data to the vm on change.
// `self` is the directive instance
// `this` points to the <select> element
self.set(select2.val());
console.log('emitting "select2-change"');
self.vm.$emit('select2-change');
})
// sync vm data change to select2
$(this.el).val(value).trigger('change')
},
unbind: function () {
// don't forget to teardown listeners and stuff.
$(this.el).off().select2('destroy')
}
})
Sample Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.user_ids"
options="globals.users.data | userList"
>
</select>
I may have found something that works alright, although I'm not sure it's the best way to go about it. Here is my updated code:
Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.reporting_type_ids"
options="globals.types.data | typeList 'reporttoauthorities'"
class="select2-users"
>
</select>
Excerpt from app.js
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
this.$nextTick(function () {
var optionsData = this.$eval('globals.users.data | userList');
console.log('optionsData', optionsData);
$('.select2-users').select2({
data: optionsData
});
});
});
},
This way works for me, but it still kinda feels hackish. If anybody has any other advice on how to do this, I would greatly appreciate it!
Thanks but I'm working on company legacy project, due to low version of select2, I encountered this issue. And I am not sure about the v-select syntax is from vue standard or not(maybe from the vue-select libaray?). So here's my implementation based on yours. Using input tag instead of select tag, and v-model for v-select. It works like a charm, thanks again #bakerstreetsystems
<input type="text"
multiple="multiple"
style="width: 300px"
v-model="supplier_id"
options="suppliers"
id="select2-suppliers"
>
</input>
<script>
$('#app').ready(function() {
var app = new Vue({
el: "#app",
data: {
supplier_id: '<%= #supplier_id %>', // We are using server rendering(ruby on rails)
suppliers: [],
},
ready: function() {
this.fetchSuppliers();
},
methods: {
fetchSuppliers: function() {
var self = this;
$.ajax({
url: '/admin_sales/suppliers',
method: 'GET',
success: function(res) {
self.suppliers = res.data;
self.$nextTick(function () {
var optionsData = self.suppliers;
$('#select2-suppliers').select2({
placeholder: "Select a supplier",
allowClear: true,
data: optionsData,
});
});
}
});
},
},
});
})
</script>

Resources