Error response won't show up using vue and blade - vue.js

I have created a small validation using laravel and vue js but when I tried to display the errors using vue it won't show up inside a div. When I check using dev tools, the messages are there but didn't display to the specified div.
Sample blade:
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in errors">#{{ errors[0]}}</li>
</ul>
</div>
Sample script:
var vm = new Vue({
el:'#app',
data: {
errors: false
},
mounted: function() {
$('.fileinput').fileinput();
},
methods: {
submitForm: function() {
vm.errors = null;
var form = document.forms.namedItem("addProjectForm");
var formdata = new FormData(form);
$.ajax({
url: $('#addProjectForm').attr('action'),
data: formdata,
contentType: false,
processData: false,
method: 'post',
error: function(data) {
if (data.status === 422) {
vm.errors = data.responseJSON;
}
},
success: function(data) {
swal("Success", "Project added successfully", "success")
}
});
}
}
})

I think here is problem :
You are overriding v-if="errors" property like this when submit data :
this.errors = null
This mean your errors are now null not true and not false right.
and because of you added v-if vue looking for errors to be true or false.
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in errors">#{{ errors[0]}}</li>
</ul>
</div>
You can fix like this :
data: {
errors: false,
error_messages: null,
},
After submit data and check error do this :
if (data.status === 422) {
vm.error_messages = data.responseJSON;
}
Then :
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in error_messages">#{{ error[0]}}</li>
</ul>
</div>
One more thing if you still want to check how it working then use v-show rather then v-if because v-if remove element and v-show will just hide and you can change behaviour by css to display:block and there will be your result. but you have to modify if you want to use with v-if;

Related

$emit is not properly triggering root method

I'm just learning vue and am having trouble with an $emit. Here's my code, with some extraneous testing clutter.
The console.log from the component is working, and the Vue console tool shows an event is happening. My root element doesn't seem to be catching the emit.
I expect a click on the input radio to call a component method changeCustomer, which then fires this.$emit('change-customer');, which I expect the root element to catch with v-on:change-customer="changeCustomer" or #change-customer="changeCustomer2 (two attempts), and then I have a console.log in each of those methods, but neither are logging.
I'm guessing it's something small, but any help is appreciated!
html:
<div id="customer-lookup"
v-on:change-customer="changeCustomer">
<label for="customer-lookup-input">Customer lookup</label>
<input type="search"
name="customer-lookup-input"
id="customer-lookup-input"
#keyUp="search"
v-model="customerLookupInput"
#change-customer="changeCustomer2"
/>
<customer-result :results="results"></customer-result>
</div>
js:
Vue.component('customer-result', {
props: {
results: {
type: Array,
required: false,
}
},
// language=Vue
template: `
<ul>
<li v-for="result in results" :key="result.id" class="customer-result">
<input type="radio"
:value="result.pk"
name="selected-customer"
v-model="customerRadio"
#change="changeCustomer"
/>
<span class="customer-name">{{ result.name }}</span>
<span class="customer-address_1" v-if="result.address_1">{{ result.address_1}}</span>
<span class="customer-address_2" v-if="result.address_2">{{ result.address_2}}</span>
<span class="customer-address_3" v-if="result.address_3">{{ result.address_3}}</span>
<span class="customer-city" v-if="result.city">{{ result.city}}, </span>
<span class="customer-state" v-if="result.state">{{ result.state}}</span>
<span class="customer-country" v-if="result.country">{{ result.country}}</span>
<span class="customer-email" v-if="result.email">{{ result.email}}</span>
<span class="customer-phone" v-if="result.phone">{{ result.phone}}</span>
</li>
</ul>
`,
data(){
return {
customerRadio: null
}
},
methods: {
changeCustomer(){
console.log('component change customer');
this.$emit('change-customer');
}
}
});
var app = new Vue({
delimiters: ['[[', ']]'], // django
el: '#customer-lookup',
methods: {
search(e){
if( this.customerLookupInput === "" ){
this.results = null;
} else {
searchCustomers(this.customerLookupInput, this); // ajax call that's working
}
},
updateResults(results){
this.results = results; // update from ajax that's working
},
changeCustomer(){
console.log('root change customer')
},
changeCustomer2(){
console.log('root2 change customer');
}
},
data() {
return {
customerLookupInput: null,
results: null,
customerRadio: null
}
}
});

How does vuejs react to component data updated asynchronously

I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.

How to toggle individual row at a time in vue for rows generated from an array? [duplicate]

I work with single file components and have a list in one of them. This list should work like a accordion, but as far as I can find in the Vuejs docs, it's not that easy to make each item open separately very easily. The data (questions and answers) is retrieved from an ajax call. I use jQuery for that, but would like to know how I can make the accordion work Vuejs-style. Any help would be appreciated!
Here's the code:
export default {
name: 'faq-component',
props: ['faqid', 'faqserviceurl', 'ctx'],
data: function () {
return {
showFaq: "",
totalFaqs: this.data,
isOpen: true
}
},
watch: {
'showFaq': function(val, faqid, faqserviceurl) {
var self = this;
$.ajax ({
url: this.faqserviceurl,
type: 'GET',
data: {id: this.faqid, q: val, scope:1},
success: function (data) {
self.totalFaqs = data;
},
error: function () {
$("#answer").html('Sorry');
}
});
}
},
methods: {
'toggle': function() {
this.isOpen = !this.isOpen
}
}
}
<template>
<div class="card faq-block">
<div class="card-block">
<form>
<div class="form-group">
<input class="form-control" type="text" placeholder="Your question" id="faq" v-model="showFaq">
</div>
</form>
<div id="answer"></div>
<ul class="faq">
<li v-for="faq in totalFaqs">
<p class="question" v-html="faq.vraag" v-bind:class={open:isOpen} #click="isOpen = !isOpen"></p>
<p class="answer" v-html="faq.antwoord"></p>
</li>
</ul>
</div>
</div>
</template>
Add an isOpen property to each object in totalFaqs and use that instead of your single isOpen property in data.
<p class="question" v-html="faq.vraag" v-bind:class={open: faq.isOpen} #click="faq.isOpen = !faq.isOpen"></p>
If you can't change the model from the server side, then add it client side.
success: function (data) {
data.forEach(d => self.$set(d, 'isOpen', false))
self.totalFaqs = data
}

Vue js attributes in xpage Vue.js

Attributes not rendered when running xpage:
:Source code:
<div id="categories" class="article-search-results-categories">
<ul class="table-view">
<li class="table-view-cell pr40" v-for="row in list.categories">
<xp:span id="clickSpan">{{row.name }}
<xp:this.attrs>
<xp:attr name="v-on:click" value="alert('ok')" rendered="true"
</xp:attr>
</xp:this.attrs>
</xp:span>
</li>
</ul>
</div>
Script
var app = new Vue ({
el: '#app',
data: {
list:[]
},
methods:{
getUsers: function(){
var link = 'https://jsonplaceholder.typicode.com/users';
this.$http.get(link).then(function(response){
this.list = response.data;
}, function(error){
console.log(error.statusText);
});
},
getCategoryJSON: function(){
var config = {
headers : {
'Content-Type' : 'application/json'
}
}
var data = {
"supplierid": "DAHL"
};
data = JSON.stringify(data);
axios.post
('ProductViewJSONCtrl.xsp',data,config).then(function(response){
app.list = response.data;
}, function(error){
console.log(error.statusText);
});
},
getLevel: function(row){
console.log('clicked');
}
},
mounted : function(){
console.log("in mounted");
this.getCategoryJSON();
}
});
Output:
<span id="view:_id1:_id2:_id295:includeBody:_id328:clickSpan">VA-
armatur/Stängventiler</span>
Any ideas why these attributes can't render?
The example with just the xpage tag is correctly generated but the problem is when the spabn is in the v-for loop
Edited m surrounding codeas the problem is when VUE.js attahces and runs for loop
This
<xp:span
id="clickSpan">
{{row.name }}
<xp:this.attrs>
<xp:attr
name="v-on:click"
value="alert('ok')"
rendered="true" />
</xp:this.attrs>
</xp:span>
renders
<span id="view:_id1:clickSpan" v-on:click="alert('ok')">{{row.name }}</span>
So, it works.
I guess you have a syntax error in source panel and your changes don't take effect yet.

Vuejs open/toggle single item

I work with single file components and have a list in one of them. This list should work like a accordion, but as far as I can find in the Vuejs docs, it's not that easy to make each item open separately very easily. The data (questions and answers) is retrieved from an ajax call. I use jQuery for that, but would like to know how I can make the accordion work Vuejs-style. Any help would be appreciated!
Here's the code:
export default {
name: 'faq-component',
props: ['faqid', 'faqserviceurl', 'ctx'],
data: function () {
return {
showFaq: "",
totalFaqs: this.data,
isOpen: true
}
},
watch: {
'showFaq': function(val, faqid, faqserviceurl) {
var self = this;
$.ajax ({
url: this.faqserviceurl,
type: 'GET',
data: {id: this.faqid, q: val, scope:1},
success: function (data) {
self.totalFaqs = data;
},
error: function () {
$("#answer").html('Sorry');
}
});
}
},
methods: {
'toggle': function() {
this.isOpen = !this.isOpen
}
}
}
<template>
<div class="card faq-block">
<div class="card-block">
<form>
<div class="form-group">
<input class="form-control" type="text" placeholder="Your question" id="faq" v-model="showFaq">
</div>
</form>
<div id="answer"></div>
<ul class="faq">
<li v-for="faq in totalFaqs">
<p class="question" v-html="faq.vraag" v-bind:class={open:isOpen} #click="isOpen = !isOpen"></p>
<p class="answer" v-html="faq.antwoord"></p>
</li>
</ul>
</div>
</div>
</template>
Add an isOpen property to each object in totalFaqs and use that instead of your single isOpen property in data.
<p class="question" v-html="faq.vraag" v-bind:class={open: faq.isOpen} #click="faq.isOpen = !faq.isOpen"></p>
If you can't change the model from the server side, then add it client side.
success: function (data) {
data.forEach(d => self.$set(d, 'isOpen', false))
self.totalFaqs = data
}