Vue v-links in JSON data - javascript

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);
}
});
},

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.

Pass variable from within script tags to Vue instance

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);
}
});

.Vue component not refreshing on each new message in laravel

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

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

Vuejs not updating list but updating obect

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"

Categories