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
Related
I am developing an application using laravel 8 and vuejs. I am trying to post form data from my vuejs to backend(laravel) but it is not working
The vuejs creates a subsection of a section which is add to an array of subsection inside the section array which is converted to string and added to a form data then sent as a request to my backend.
The frontend is working perfectly well but I cant access the data on my backend. How do I get the values of the course title, section title, sub section title and file added
Vuejs
<script>
import { reactive } from "vue";
import axios from "axios";
export default {
name: 'CreateCourse',
setup(){
const sections = reactive([{'section_title': '', 'sub_sections': [{'sub_section_title': '', 'file': '', 'url': ''}]}]);
const course = reactive({'title': '', 'description': ''});
const addSection = () => {
sections.push({"section_title": "", 'sub_sections': [{'sub_section_title': '', 'file': '', 'url': ''}]});
}
const addSubSection = (idx) => {
console.log('the value of idx is ', idx);
sections[idx].sub_sections.push({"sub_section_title": "", 'file': '', 'url': ''});
}
const uploadFile = (e, idx, i) => {
sections[idx].sub_sections[i].file = e.target.files[0];
sections[idx].sub_sections[i].url = URL.createObjectURL(sections[idx].sub_sections[i].file);
}
const createCourse = (e) => {
e.preventDefault();
let newCourse = JSON.stringify(course)
let newSection = JSON.stringify(sections)
const formData = new FormData();
formData.append("course", newCourse);
formData.append("sections", newSection);
showLoader(true);
axios.post('/api', form, { headers: {'Content-Type': 'multipart/form-data'}}).then(response =>
{
NotificationService.success(response.data.message);
showLoader(false);
course.title = '';
course.description = '';
}).catch(err => {
NotificationService.error(err.response);
showLoader(false);
});
}
return {
course,
createCourse,
sections,
addSection,
addSubSection,
uploadFile
}
}
</script>
laravel code
echo $request->get("title");
echo $request->get("description");
foreach($request->section_title as $titles)
{
echo $titles
}
foreach($request->section_sub_title as $sub_titles)
{
// info($sub_titles);
// return $sub_titles;
echo $sub_titles
}
{"course":{"title":"Frontend","description":"This is building web interface with html, css and javascript"},"sections":[{"section_title":"HTML","sub_sections":[{"sub_section_title":"What is HTML","file":{},"url":"blob:http://localhost:8080/ea0acc7d-34e6-4bff-9255-67794acd8fab"}]}]}
Bit tricky to understand where you're stuck, but let's give it a shot:
Does the api request actually reach your route (post -> /api), do you see in the network tab a post request to the route?
Have you tried running dd($request->all()) in the controller method so see what you're getting (just do this on the first line inside your method)?
Small gotcha moment:
Sometimes it helps to run the php artisan route:clearcommand
I am building a trivia maker. I am currently working on the edit page for a question. The edit component, named EditQAForm, grabs the question and answers for that particular question and populates each of it's respective VueX store's form.
I am currently having trouble with the answers portion of this page. When the EditQAForm is mounted it calls the fetchQuestionAnswers, which retrieves all the answers for that particular question. It does this correctly, but then when I try to display any of the answers onto the page, it says that the form is empty despite me seeing in the Vue DevTools that it is not empty.
(Please note I deleted stuff that wasnt relevant to this. So assume all methods you see called do exist)
Here is the mounted for the EditQAForm:
mounted() {
//gets the params from the url
this.routeParams = this.$route.params;
//gets the answers that belong to this question
this.fetchQuestionAnswers(this.routeParams.question_id);
//not important for this problem
//get the question that needs to be edited
this.fetchQuestion(this.routeParams.question_id);
},
How I call it in the computed properties of the EditQAForm:
computed: {
...mapGetters('question', ['formQuestionRoundID', 'questions', 'questionFields']),
...mapGetters('answer', ['answerFields', 'answers']),
//Questions
questionForm: {
get() {
return this.questionFields;
},
},
//Answers
answerForm: {
get() {
return this.answerFields;
},
},
}
Here is the store for the answers
function initialState() {
return {
answers: [],
answer: null,
form: [
{
id: '',
title: '',
question_id: '',
round_id: '',
correct: false,
},
{
id: '',
title: '',
question_id: '',
round_id: '',
correct: false,
},
{
id: '',
title: '',
question_id: '',
round_id: '',
correct: false,
},
]
}
}
const getters = {
answers(state){
return state.answers;
},
answerFields(state){
return state.form;
},
loading(state){
return state.loading;
},
};
const actions = {
fetchQuestionAnswers({ commit, state }, question_id) {
console.log("Form outside axios:");
console.log(state.form);
commit('setLoading', true);
axios.get('/api/question/' + question_id + '/answers')
.then(response => {
commit('SET_ANSWERS_FORM', response.data);
commit('setLoading', false);
}).catch( error => {
console.log(error.response);
});
},
const mutations = {
SET_ANSWERS_FORM(state, answers){
for(let $i = 0; $i < answers.length; $i++)
{
state.form[$i] = {
id: answers[$i].id,
title: answers[$i].title,
question_id: answers[$i].question_id,
round_id: answers[$i].round_id,
correct: answers[$i].correct,
}
}
// state.answers = answers;
},
UPDATE_TITLE(state, payload){
state.form[payload.order].title = payload.title;
},
UPDATE_QUESTION_ID(state,payload){
state.form[payload.order].question_id = payload.questionID;
},
};
What I try outputting:
<div>
<h3 class="pb-3">Is first answer's title not empty?: {{!(answerForm[1].title === '')}}</h3>
<h3 class="pb-3">{{answerForm[0].title }}</h3>
<h3>{{answerForm}}</h3>
</div>
What shows on my screen, alongside what devtools tells me is inside the answerForm array:
I implemented the question portion in a very similar way. The only difference is that the form is not an array in the question store, but besides that it works fine. What am i doing wrong?
I think the problem is here:
state.form[$i] = {
If you use an index to update an array it won't trigger the reactivity system and you'll get a stale version of the rendered components. See https://v2.vuejs.org/v2/guide/list.html#Caveats
There are various ways to fix this. You could use Vue.set or alternatively just create am entirely new array.
Not entirely clear to me why you're doing all that copying in the first place rather than just using state.form = answers, which would also solve the problem.
I want to skip all video type posts from a feed that I'm gathering through the Instafeed JS plugin. Read from a few other posts that setting a filter would solve it but if I apply this (see below) I only get 2 images instead of 5. 1 of those 5 are a video type and the rest are image types. Not sure whats going on here?
var loadButton = document.getElementById('instafeed-loadmore');
var feed = new Instafeed({
get: 'user',
type: 'image',
limit: '5',
sortBy: 'most-recent',
resolution: 'standard_resolution',
userId: '',
accessToken: '',
template: '<div><img src="{{image}}" data-etc=""></div>',
filter: function(image) {
return image.type === 'image';
},
after: function() {
if (!this.hasNext()) {
loadButton.setAttribute('disabled', 'disabled');
}
},
});
loadButton.addEventListener('click', function() {
feed.next();
});
Maybe removing the resolution parameter should help. Also I dont think
type: 'image',
is a valid argument. I cant find it in the instafeed documentation as well.
TRy following
var feed = new Instafeed({
get: "user",
userId: "xxxx",
accessToken: "xxxx",
filter: function(image) {
if (image.type === "image") {
return false;
}
return true;
}
});
feed.run();
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'));
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>