How can I use "<nuxt-link>" in content rendered with "v-html"? - vue.js

I have a utilities plugin for Nuxt.js that I created with a method to parse hashtags and mentions and replace them with <nuxt-link>. I am then using v-html to inject the converted data back into the template. My issue is that the <nuxt-link> are not being parsed with v-html.
import Vue from 'vue';
Vue.mixin({
methods: {
replaceMentions(data) {
// Tags
const tagRegEx = /\[#tag:[a-zA-Z0-9_-]*\]/g;
let tagMatch;
while ((tagMatch = tagRegEx.exec(data)) !== null) {
const tag = Array.from(tagMatch[0].matchAll(/\[#tag:(.*?)\]/g));
data = data.replace(tag[0][0], '<nuxt-link to="/search?q=' + tag[0][1] + '">#' + tag[0][1] + '</a>');
};
// Users
const userRegEx = /\[#user:[a-zA-Z0-9_-]*\]/g;
let userMatch;
while ((userMatch = userRegEx.exec(data)) !== null) {
const user = Array.from(userMatch[0].matchAll(/\[#user:(.*?)\]/g));
data = data.replace(user[0][0], '<nuxt-link to="/users/' + user[0][1] + '">#' + user[0][1] + '</>');
};
return data;
}
}
});
Does anyone have any tips for how I could make these work as proper nuxt compatible links? I already tried using <a> and it works fine, I would just prefer to utilize proper nuxt compatible links.

I think the discussion here basically answers the question: https://github.com/nuxt-community/modules/issues/185
Summarized, there are two options:
Render the HTML with a full Vue build and then attach the rendered node.
(Preferred) Find the links in the HTML and make them call router push instead of their default action.

Related

Stuck with $ref pointing to Proxy object with vue-router

I was using VueJS in browser mode and am now trying to switch my code to a VueJS SPA and vue-router. I've been stuck for hours with a $refs not working anymore.
To interact with my Google Charts, I was using an absolute reference to the graph (this.$refs.villesChart) to get selected data like that:
computed: {
eventsprox() {
let eventsprox = {
select: () => {
var selection = "";
if (this.$refs.villesChart) selection = this.$refs.villesChart1.chartObject.getSelection();
if (selection.length) {
var row = selection0[0].row + 1;
this.code_commune = this.dataprox[row][4];
this.changerville(this.code_commune, this.dataprox[row][0]);
}
return false;
},
};
return eventsprox;
}
HTML code for graph:
<GChart type="BarChart" id="villesChart" ref="villesChart" :data="dataprox" :options="optionsprox" :events="eventsprox"/>
I don't know why, but in browser mode, this.$refs.villesChart is a component:
[1]: https://i.stack.imgur.com/xJ8pV.png
but now it is a proxy object, and lost its chartObject attribute:
[2]: https://i.stack.imgur.com/JyXrL.png
I'm really confused. Do you have an idea why?
And if I use the proxy object, then I get a Vue warning "Avoid app logic that relies on enumerating keys on a component instance" and it is not working in production environment.
Thanks a lot for your help!!
After hours of testing different solutions, I finally found a solution working with Vue3 and Vue-google-chart 1.1.0.
I got rid of "refs" and put the events definition and code in the data section of my Vue 3 app (instead of computed) and accessed the chart data through a component variable I used to populate it.
Here is my event code where this.dataprox is my data table for the chart:
eventsprox: {
'click': (e) => {
const barselect = parseInt(e.targetID.split('#')[2]) + 1;
this.code_commune = this.dataprox[barselect][4];
this.nom_commune = this.dataprox[barselect][0];
this.changerville(this.code_commune, this.nom_commune);
}
},
My Gchart html code:
<GChart type="AreaChart" :data="datag" :options="optionsg" :events="eventsprox"/>
I hope it can help!

New Web NG SDK (Using with Vue.js)

I notice a new web SDK for web(NG). Looks good. I have looking to use it with a Vue application.
To display a video stream the function needs the ID if a Div container. e.g.
this.localVideoTrack.play("local-video")
Vue does not work this way (i.e. manipulate directly via dom elements, does anybody know how I can update a Vue component to display the video stream?
Using the .play() method and passing in the id of the div you wish to use does work in Vue, you can check this Agora Vue Demo App, check Line 337 within AgoraVideoCall.vue
$.streamList.map((item, index) => {
let id = item.getId();
let dom = document.querySelector("#ag-item-" + id);
if (!dom) {
dom = document.createElement("section");
dom.setAttribute("id", "ag-item-" + id);
dom.setAttribute("class", "ag-item");
canvas.appendChild(dom);
item.play("ag-item-" + id);
}
if (index === no - 1) {
dom.setAttribute("style", `grid-area: span 12/span 24/13/25`);
} else {
dom.setAttribute(
"style",
`grid-area: span 3/span 4/${4 + 3 * index}/25;
z-index:1;width:calc(100% - 20px);height:calc(100% - 20px)`
);
}
item.player.resize && item.player.resize();
});

How Vue knows which prefix should prepend when different browser?

Like
<template>
<h1 :style={ filter: 'blur(1px)' }>My Template!!</h1>
</template>
I used style and webkit to search source code from node_modules/Vue and node_modules/#Vue, but had no luck.
How Vue knows which prefix should prepend when different browser?? So magic it is!!
https://v2.vuejs.org/v2/guide/class-and-style.html#Auto-prefixing
I suppose I found the answer.
The code is under vue/src/platforms/web/runtime/modules/style.js line 32
const vendorNames = ['Webkit', 'Moz', 'ms']
let emptyStyle
const normalize = cached(function (prop) {
emptyStyle = emptyStyle || document.createElement('div').style
prop = camelize(prop)
if (prop !== 'filter' && (prop in emptyStyle)) {
return prop
}
const capName = prop.charAt(0).toUpperCase() + prop.slice(1)
for (let i = 0; i < vendorNames.length; i++) {
const name = vendorNames[i] + capName
if (name in emptyStyle) {
return name
}
}
})
The emptyStyle here is CSSStyleDeclaration from browser.
Vue will check every attribute with prefix in CSSStyleDeclaration or not.
If yes then will append it and cache it.
However, it looks like the filter attribute is an exception here.
Most of CSS we will write in CSS file then it will be compiled by PostCSS and Autoprefixer. Consider the runtime, the code above I guess is the easiest and smallest way to achieve, yet still have some surprises.

Adding auto-ads with Vue.js

I have tried to add auto-ads to my website which uses Vue.js but there are no requests via the script.
I have tried the vue-adsense plugin, which can be found on npm https://www.npmjs.com/package/vue-google-adsense
but they have no support for auto-ads, normal ads work fine with this plugin.
This is the code which needs to be added:
<script data-ad-client="ca-pub-0990618353003742" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
Is there a solution for adding auto-ads on a vue.js site?
Script tags cannot be placed in a part of the DOM controlled by an instance of Vue(). All you need to do is place the script in the <head> of your document. If you are using Vue CLI to create your project, the file you do this in is /public/index.html.
You can init your ads how many you've added after change data. For example.
mounted: function () {
this.data = [1,2,3,4];
var adCount=3;
for (var i = 0; i < parseInt(adCount); i++) {
window.setTimeout(function () {
console.log('vue ad ' + i + ' inited');
(adsybygoogle = window.adsybygoogle || []).push({});
}, 300);
}
},

Riot JS unmount all tags in a page and then mount only one tag is not working

I am using Riot JS and in my index.html, I have 3 custom tags - header, login-panel and candidates-panel inside my body. In my main app.js, in the callback function of $(document).ready, I execute the current route and also register a route change handler function. In my switchView, I unmount all custom tags and then try to mount only the tag pertaining to the current view being switched. Here is my code. If I do unmount, then nothing is displayed on the page
index.html
<body>
<header label="Hire Zen" icon="img/user-8-32.png"></header>
<login-panel class="viewTag" id="loginView"></login-panel>
<candidates-panel id="candidatesView" class="viewTag"></candidates-panel>
<script src="js/bundle.js"></script>
</body>
app.js
function switchView(view) {
if(!view || view === '') {
view = 'login'
}
//unmount all other panels and mount only the panel that is required
//TODO: unmount all view panels and mounting only required panel is not working
//riot.unmount('.viewTag')
riot.mount(view+'-panel')
$('.viewTag').hide()
$(view+'-panel').show()
}
$(document).ready(function () {
RiotControl.addStore(new AuthStore())
RiotControl.addStore(new CandidatesStore())
riot.mount('header')
//register route change handler
riot.route(function (collection, id, action) {
switchView(collection)
})
riot.route.exec(function (collection, id, action) {
switchView(collection)
})
})
Answer for riot.js v2.1.0:
The function
riot.unmount(...)
is not available as far as I know. However, you can unmount saved tags.
mytag.unmount(true)
Source
The trick is to remember the mounted tags to be able to unmount them later:
var viewTag = riot.mount(document.getElementById('viewTag'))
viewTag.unmount(true)
You can store all those view tags in an object and loop them to unmount all and mount only the active one.
Source
Answer for 2.3.18
Based on the previous answer and this tutorial I have created following concept:
app.currentPage = null;
var goTo = function(page){
if (app.currentPage) {
app.currentPage.unmount(true); //unmount and keep parent tag
}
app.currentPage = riot.mount(page)[0]; //remember current page
};
riot.route(function() {
console.info("this page is not defined");
//do nothing (alternatively go to 404 page or home)
});
riot.route('/inventory', function(){
goTo('inventory');
});
riot.route('/options', function() {
goTo('options');
});
I think you are looking for riot.util.tags.unmountAll(tags)
How to achieve the goal?
index.html
var tags = [];
some.tag.html
var some = this;
tags.push(some);
unmountAllTags.js
riot.util.tags.unmountAll(tags);