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
Related
In my Drupal 7 site's html I have this
<script>$L = $L.wait(function() {
(function($) {
Drupal.behaviors.related_products = {
attach: function (context, settings) {
artiklar = Drupal.settings.related_products.artiklar;
console.log(artiklar);
}
};
})(jQuery);
});</script>
In the variable artiklar above I have some data that I have passed from the server side using Drupal behaviors. Now, on the client side I need to access the variable artiklar in a Vue component, like so:
Vue.component('artikel-lista', {
template:`
<ul>
<artikel v-for="artikel in artiklar">{{ artikel.title }} Pris: {{artikel.price}} <a :href="artikel.link" class="button tiny" target="_blank">Läs mer</a></artikel>
</ul>
`,
data(){
return {
artiklar: "",
};
},
mounted: function(){
this.artiklar = artiklar // how can I access the variable "artiklar" here
},
});
The data in the variable consists of an array of items, that I need in my Vue component. But how can I pass the variable from within the script tags to the Vue instance, that lives in a separate file, inserted just before the ending body tag. Anyone?
If you have data in the globally visible Drupal.settings.related_products.artiklar object then you can refer to it practically the same way in Vue.js. or if you must use this function, assign data to global scope window.*.
new Vue({
template: `<div>{{foo}} / {{bar}}</div>`,
data() {
return {
foo: Drupal.settings.related_products.artiklar,
bar: window.artiklarData
};
}
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">Vue App</div>
<script>
// simulate global variable
var Drupal = {
settings: {
related_products: {
artiklar: ['fus', 'ro', 'dah']
}
}
};
(function() {
window.artiklarData = Drupal.settings.related_products.artiklar;
})();
</script>
If you assign the value to Drupal.settings.related_products.artiklar after creating the Vue object, you can try to use the solutions described in the documentation, e.g.
const vm = new Vue({
template: `<div>{{foobar}}</div>`,
data() {
return {
foobar: 'Initial value'
};
}
}).$mount("#app");
setTimeout(() => {
// simulate global variable
var Drupal = {
settings: {
related_products: {
artiklar: 'Changed value'
}
}
};
(function() {
vm.foobar = Drupal.settings.related_products.artiklar;
})();
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">Vue App</div>
Maybe you could use RxJS but I don't have enough knowledge to tell if it's true and give an example.
Just in case anyone else is struggling with the same thing, I post this answer to my own question (I accidentally posted the question with the wrong account). In the end it turns out that the answer from Gander was correct and that I could access the variable directly in the Vue component, w/o first stashing it an a global variable. The viewed result was kind of weird though and after some trialling I found out that I had to parse the result with JSON.parse(). This is the working code now:
Vue.component('artikel-lista', {
template:`
<ul>
<artikel v-for="artikel in artiklar">{{ artikel.title }} Pris: {{artikel.price}} <a :href="artikel.link" class="button tiny" target="_blank">Läs mer</a></artikel>
</ul>
`,
data(){
return{
artiklar:""
}
},
mounted:function(){
this.artiklar = JSON.parse(Drupal.settings.related_products.artiklar);
console.log(this.artiklar);
}
});
I followed the following tutorial to create a chat application.
https://github.com/ammezie/laravel-chat
Every thing is right, messages are storing in db , showing on console in pusher desktop, all message show on page re load.
Problem is when i send a new message it not show in other user tab until i reload the page. I need to make it dynamic
following is the code for app.js where fetch function is written
created() {
this.fetchMessages();
Echo.private('chat')
.listen('MessageSent', (e) => {
this.messages.push({
message: e.message.message,
user: e.user
});
});
},
methods: {
fetchMessages() {
axios.get('/messages').then(response => {
this.messages = response.data;
});
},
addMessage(message) {
this.messages.push(message);
axios.post('/messages', message).then(response => {});
}
here
Following is chat view code of component
<template>
<ul class="chat">
<li class="left clearfix" v-for="message in messages">
<div class="chat-body clearfix">
<div class="header">
<strong class="primary-font">
{{ message.user.name }}
</strong>
</div>
<p>
{{ message.message }}
</p>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ['messages']
};
</script>
Thanks for help if some thing unclear i'll provide
You can try.
methods: {
var vm = this;
fetchMessages() {
axios.get('/messages').then(response => {
vm.messages = response.data;
});
},
addMessage(message) {
var vm = this;
vm.messages.push(message);
axios.post('/messages', message).then(response => {});
}
using this inside function causing a problem, because it refers to that particular function create a global variable with reference to this
Hope this helps.
You may want to check if there are Laravel Echo credentials for pusher correct in bootstrap.js
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"
I have the following JS that I'm trying to translate to Vue JS. I was told a computed property would be the way to go. I'm a little confused on computed properties still. I know how I want it to function, I want it's input to be the key of the item in the list. And I want the output to be a 'related' array that I can then loop through in sub list. If you are having trouble visualizing, please see this CodePen Any help would be awesome.
// Initialize Firebase
const db = firebase
.initializeApp({
databaseURL: "...firebaseio.com"
})
.database();
const contentRef = db.ref("a");
const relatedRef = db.ref("test");
new Vue({
el: "#app",
data: {
related: {}
},
firebase: {
content: contentRef,
related: relatedRef
},
});
var theKey = "objectb"
function getDiscoverBarContent(key, cb) {
relatedRef.child(key).on("child_added", snap => {
let relateRef = contentRef.child(snap.key);
relateRef.once("value", cb);
});
}
getDiscoverBarContent(theKey, snap => {
var snapVal = snap.val();
console.log(snapVal)
});
HTML
<div id="app">
<div>
<h2>Content</h2>
<pre><code>{{content}}</code></pre>
</div>
<div>
<h2>'Related' Content</h2>
<pre><code>{{related}}</code></pre>
</div>
<div>
<h2>'Results'</h2>
<ul>
<li v-for="thing in content">{{thing.name}}
<ul>
<li v-for="relation in related">{{relation.name}}</li>
</ul>
</li>
</ul>
</div>
</div>
EDIT: My attempt: CodePen
computed: {
relatedThings: function () {
var theKey = "objectb"
var output = []
function getDiscoverBarContent(key, cb) {
relatedRef.child(key).on("child_added", snap => {
let relateRef = contentRef.child(snap.key);
relateRef.once("value", cb);
});
}
getDiscoverBarContent(theKey, snap => {
var snapVal = snap.val();
console.log(snapVal)
var output = snapVal
});
return snapVal;
}
}
And Error:
ReferenceError: snapVal is not defined.
Please note, this is my attempt and I am not certain that it is going to work.
I should add...my JS skills are lacking. I know why this issue is happening but not sure how to fix.
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);
}
});
},