Vuejs not updating list but updating obect - javascript

I am learning Vuejs and I am stuck. Why can I see the messages get added to the object (in Chrome Vue debugger) yet it is not added to the div that contains the list?
My Vue Component:
<template>
<div id="round-status-message" class="round-status-message">
<div class="row">
<div class="col-xs-12" v-for="sysmessage in sysmessages" v-html="sysmessage.message"></div>
</div>
</div>
</template>
<script>
export default {
props: ['sysmessages'],
methods: {
scrollToTop () {
this.$el.scrollTop = 0
}
}
};
</script>
My Vue instance:
$(document).ready(function()
{
Vue.component('chat-system', require('../components/chat-system.vue'));
var chatSystem = new Vue({
el: '#system-chat',
data: function () {
return {
sysmessages: []
};
},
created() {
this.fetchMessages();
Echo.private(sys_channel)
.listen('SystemMessageSent', (e) => {
this.sysmessages.unshift({
sysmessage: e.message.message,
});
this.processMessage(e);
});
},
methods: {
fetchMessages() {
axios.get(sys_get_route)
.then(response => {
this.sysmessages = response.data;
});
},
processMessage(message) {
this.$nextTick(() => {
this.$refs.sysmessages.scrollToTop();
});
// updateGame();
}
}
});
});
My template call in HTML:
<div id="system-chat">
<chat-system ref="sysmessages" v-on:systemmessagesent="processMessage" :sysmessages="sysmessages" :player="{{ Auth::user() }}"></chat-system>
</div>
There are no compile or run time errors and I can see records added to the props in the vue chrome tool. I can also see empty HTML elements added to the div.
What have I missed?
UPDATE: My record structures:
response.data is an array of objects, each like this:
{"data":[
{"id":100,
"progress_id":5,
"message":"start message",
"action":"welcome"
},
{"id"....
e.message.message contains the text message entry, so just a string.
I am trying to access the message variable in each object during the fetchMessages method.

You're adding objects with sysmessage as the property.
this.sysmessages.unshift({
sysmessage: e.message.message,
});
But you are trying to view
v-for="sysmessage in sysmessages" v-html="sysmessage.message"
Based on your update, the code should be:
this.sysmessages.unshift({
message: e.message.message,
});
And you can leave the template as
v-html="sysmessage.message"

Related

Vue.js return component value to vanilla javascript

I'm wondering if I am able to get a components data, the count property in this instance and console.log it into normal javascript, is this possible? I'm wanting to do console.log(btn.data.count) in this case
<div id="app" v-cloak>
<h1>{{greeting}}</h1>
<button-counter></button-counter>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script>
let app = Vue.createApp({
data: function(){
return {
greeting: "hi"
}
}
})
let btn = app.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
console.log(btn.data.count) // doesn't work
app.mount("#app")
</script>
There might be multiple instances of the button-counter component, so you cannot ask for the count.
You can only access the data from within the component itself. For example from within a method that handles the click event:
let btn = app.component('button-counter', {
data: function () {
return {
count: 0
}
},
methods: {
onClick() {
console.log(this.count)
this.count++
}
},
template: '<button v-on:click="onClick">You clicked me {{ count }} times.</button>'
})
You can use console.log normally you just need to use it where it makes sense. It does not make sense using it before #app is mounted.
Try writing a method for your count++ and add console log there you will see that it gets executed every time.

DOM not updating in vue.js even when data changes

The changes are not reflected in DOM, even when the data is changing properly. Here is a very basic example to demonstrate the issue -
<template>
<input type="text" v-model="username" />
<p>{{error}}</p>
<button #click="saveData">Save</button>
</template>
<script>
export default {
data () {
model.error = ''; // adding a new property
return model; // 'model' is a global variable
}
methods: {
saveData() {
if (!this.username) {
this.error = 'Please enter the username!';
return;
}
// ... other code
}
}
};
</script>
After calling saveData() the error variable contains the message if username is not filled. But it's not showing up in the paragraph.
There is a trick. If I also change the username property when the error variable is changed, the changes are reflected.
You need to return error or Vue doesn't have access to it.
data () {
return {
error: '',
model: model,
};
}
You should be able to achieve what you're trying to do, as long as error and username properties are defined on model for data. I've included a simple snippet below, showing it working. Take a look at Declaring Reactive Properties in the documentation.
var model = {
username: "Default"
};
new Vue({
el: "#app",
data: () => {
model.error = model.error || "";
return model;
},
methods: {
updateError() {
this.error = "Test";
},
updateUsername() {
this.username = "Hello, World!";
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button type="button" #click="updateError">Update Error</button>
<button type="button" #click="updateUsername">Update Username</button>
<div>Error: {{error}}</div>
<div>UserName: {{username}}</div>
</div>

How to use List.JS with Vue + Axios?

I have used List.JS before successfully, but this time I'm trying to use it with a Vue.JS rendering of a list from JSON data.
I have a button at the top that when clicked should show only the QB position player.
Unfortunately I just get nothing, all list items are removed and I don't get an error in the console so I'm not sure how to diagnose this.
Could it have something to do with the fact that the list elements aren't prerendered/static html but injected using vue.js?
https://jsfiddle.net/nolaandy/hw2mheem/
HTML/Vue Template
<div id='app'>
<div class="all-players-wrapper" id="all-player-listings">
<button id="filter-qb">QB</button>
<ul class="list">
<li v-for="player in playerJSON">
<div class="player-listing">
<div class="player-left">
<div class="player-name">{{player.firstName}} {{player.lastName}}</div>
<div class="playerPosition">{{ player.Position }}</div>
</div><!-- end player-left -->
<div class="player-right">
<div class="player-grade">GRADE <span>{{player.NFLGrade}}</span></div>
</div> <!--end player-right -->
</div>
</li>
</ul>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
status: 'Combine Particpants',
playerJSON: []
},
created: function () {
this.loadData();
},
methods: {
loadData: function () {
var self = this;
axios.get('https://s3-us-west-2.amazonaws.com/s.cdpn.io/500458/tiny.json').then(function (response) {
self.playerJSON = response.data
console.log(response.data);
})
.catch(function (error) {
self.status = 'An error occurred - ' + error
});
}
}
});
var options = {
valueNames: [ 'playerPosition' ]
};
var featureList = new List('all-player-listings', options);
$('#filter-qb').click(function() {
featureList.filter(function(item) {
if (item.values().playerPosition == "QB") {
return true;
} else {
return false;
}
});
return false;
});
As you suspected, List.js isn't going to work properly if the DOM changes unpredictably. In this case, axios makes its call and populates the data after the (empty) List has been read into featureList.
Your example would work if you put the list-selecting-and-filtering code in the resolution of the axios call, but that's not going to be a solution that works in a truly dynamic environment.
A custom directive will be called every time the DOM updates, so you can apply your adjustments consistently. Here's a directive to apply a filter using List.js:
directives: {
filteredList(el, binding) {
if (binding.value) {
const options = {
valueNames: ['playerPosition']
};
const featureList = new List(el, options);
featureList.filter((item) => item.values().playerPosition === binding.value);
}
}
}
Apply it like so:
<div class="all-players-wrapper" v-filtered-list="filterValue">
Add the filterValue data item, and have the button set it:
<button id="filter-qb" #click="() => filterValue='QB'">QB</button>
and you're in business.
It's worth noting that you could get the same effect by using a computed to filter the data, and you wouldn't need an external library.
Updated fiddle

How to Re-bind / Refresh Dom after ajax call in vuejs2 (like $scope.$apply in angularjs)?

I am using vue.js 2 with webpack template. i am new to vue.js.
i want to bind DOM after data is received in ajax call.
here in my code contains v-for in which the response html is need to bind with v-html="tHtml[index]" every time after data is received from api call.
What can be used to re-bind or refresh view/DOM as we use $scope.$apply() in angularjs.
home.vue
<template>
<div class="row" id="home">
<h3 v-if="msg"><span class="label label-warning">{{msg}}</span></h3>
</div>
<div v-for="obj in tdata">
<div v-html="tHtml[$index]"></div>
</div>
</div>
</template>
<script>
import appService from '../service'
import bus from '../service/bus'
export default {
created() {
appService.checkAuth();
console.log("service appService val: "+appService.user.authenticated);
//called after vue has been created
bus.$on('eventBusWithTopic', (data) => { //fetch event bus on ready to get data
//alert("event fetched! "+data.user_id);
console.log("event fetched! "+data.user_id);
this.searchedTopic = data.topic;
this.user_id = data.user_id;
bus.$off('eventBusWithTopic'); //un-bind event after use
//check details
})
},
data() {
return {
searchedTopic: '',
user_id: '',
tdata: {},
tHtml: []
}
},
methods: {
getdata(){
console.log("dfd "+this.user_id);
appService.getdata(this, this.user_id, this.searchedTopic).success((res) => {
//console.log(" res: "+JSON.stringify(res));
this.tdata = res.data;
if(this.tdata.length > 0){
//**GET HTML** // <<--==--==--==
for(var i=0;i<this.tdata.length;i++){
this.getTemplate(this.tdata[i].id_str);
}
}
if(res.success == 0)
this.msg = res.message;
}).error((e) => console.log("error while getting data: "+JSON.stringify(e)))
},
getTemplate(id){
this.$http.get("https://uqkcgyy8wb.execute-api..*****../user/"+id+"/getHtml", (resp) => {
//return resp.data.html;
this.tHtml[this.tHtml.length] = resp.data.html;
console.log(this.tHtml);
}).error((e) => console.log("error while getting html: "+JSON.stringify(e)))
}
}
}
</script>

Vue v-links in JSON data

I am relatively new to Vue, so forgive me if this is obvious (or obviously impossible).
I have a set of JSON data (fetched from a RESTful API via vue-resource):
{content: "This is content. <a href='/blog'> Link to blog </a>"}
Right now, the link triggers a page reload. If it were a vue-router v-link, that would not be an issue. However, this doesn't work (quotes are escaped in the data, of course):
{content: "This is content. <a v-link="{ path: '/blog' }"> Link to blog </a>"}
At this point, the template is already parsed, and Vue won't create a v-link anymore (it will just show up as a v-link in the rendered html).
My final result would ideally mean that I could include links in my CMS, either in HTML or Vue format, and have Vue route them correctly as v-links.
Is there something I can do to make Vue interpret the link in the JSON data?
I've answered the question on Vue Chat, and writing it here in case any other people facing similar problem
Simplified example on Codepen
HTML
<div id="app">
<div>
<a v-link= "{path:'/home'}">Go to home</a>
</div>
<router-view></router-view>
</div>
<template id="home">
<div>
<div>
Fetched Content:
</div>
<div>
{{{ fetchedContent }}}
</div>
</div>
</template>
<template id="route1">
<div>
Route1 view
</div>
</template>
<template id="route2">
<div>
Route2 view, this is different from Route1
</div>
</template>
javascript
function getContent (callback) {
var content = 'Click this: Go to route1 and Go to route2'
setTimeout(function () { callback(content) }, 1000)
}
var Home = Vue.component('home',{
template:'#home',
data: function () {
return {
fetchedContent: 'Loading...'
};
},
ready: function () {
var self = this
var router = this.$router
getContent( function (result) {
self.fetchedContent = result;
Vue.nextTick(function () {
var hyperLinks = self.$el.getElementsByTagName('a')
Array.prototype.forEach.call(hyperLinks, function (a) {
a.onclick = function (e) {
e.preventDefault()
router.go({ path: a.getAttribute("href") })
}
})
})
})
}
});
var Route1 = Vue.component('route1', {
template: '#route1'
});
var Route2 = Vue.component('route2', {
template: "#route2"
});
var router = new VueRouter({
hashbang:false,
history:true
});
router.map({
'/home':{
component:Home
},
'/route1':{
component:Route1
},
'/route2':{
component:Route2
}
});
router.start({
}, '#app');
I had a similar solution here: question using a custom dataset in my JSON code and a click listener to process it:
mounted() {
window.addEventListener('click', event => {
let target = event.target
if (target && target.href && target.dataset.url) {
event.preventDefault();
console.log(target.dataset.url);
const url = JSON.parse(target.dataset.url);
console.log(url.name)
this.$router.push(url.name);
}
});
},

Categories