How to add router-link in vue js dynamically - vue.js

I am facing a very interesting problem. I am returning some html from server as json. My return html string look like this
str2: " <span class="card_top_com_span link_color" ><router-link to="/profile/sadek3/about">numan sir</router-link> </span></span>, <span class="card_top_com_span link_color" ><router-link to="/profile/sadek3/about">sadek mia</router-link> </span> and 4 of your firiends commented on this post"
This is returned from server. Now I want to add some spa link.
It can be nuxt link, #click event for routing or a </router-link>
I am using v-html in my front end to out put html. It does output correctly.
Is there anyways of doing this?
Thank you.

As said in the comments, it's way better to respond from your server with structured JSON data. However, you can make it work, but you need to use a <component></component>. Just using v-html won't work if you have router-link:
<div id="app">
<component :is="{template: theString}"></component>
</div>
new Vue({
el: '#app',
data: {
theString: '<h3>Something Cool</h3>'
}
})
https://jsfiddle.net/to8smxfb/
PS: You also need to make sure that theString only contains one root element. You can wrap your string into <div></div> for example.

Related

Vue Custom directive not working with template tag

I'm facing issue with the below directive, it is working with any tag except the template tag
Vue.directive('count',{
bind(el,b,v){
console.log(b.value);
},
inserted(el,b,v){
console.log(b.value);
},
update(el,b,v){
console.log(b.value);
}
});
new Vue({
el: "#app" ,
data:{
value:0
}
})
<div id="app">
<button #click="value+=1">
inc value
</button>
<template v-count="value">
</template>
<!-- <div v-count="value">
</div> -->
</div>
If the comment on the div is removed, the directive logs the value, but with the template tag directive's hooks are not triggered
Here jsfiddle for the issue
Thanks
The template tag doesn't actually mount anything on its own, so it has no place in the DOM and doesn't bind. In your case, an easy solution is to just put v-count="value" on the button tag. If there is a more specialized need for this directive and you're just giving us placeholder code, putting it on a SPAN or DIV will make more sense than a template anyway.

How to render VueJs component based child node text?

======Updated with more background=====
I am working on a tool to convert text to a sequence diagram like this:
In the current implementation, the code (on the left) is provisioned programmatically by calling store.dispatch. I would like to make it simpler for other projects to integrate. What I wanted to achieve is to create a web component: <sequence-diagram />. It can be used in this way:
// Format A
<sequence-diagram>
response = BookController.Get(id)
....
</sequence-diagram>
The above DOM element would be rendered as a sequence diagrams (as shown on the right side of the above picture.
For the component to render properly, it needs to know what the "code" is. To pass the code (response = ....) to the component, I know I can use attributes and access it via props like this:
// Format B
<sequence-diagram code="response = ..." />
However, when the code is very long the above format is not as readable (imagine multiline code) as putting the code as child node text. If I use "Format A", how can I get the code content in my web component?
======Original question=====
What I want to implement is like this:
<my-component>
some text
</my-component>
I have managed to make it work by using attributes:
<my-component code="some text" />
Using child node text is much more readable in my case, as the text can be very long.
In the template, it already has a child component. The current template is like
<div> <myChildComponent/> </div>
I don't need to keep the text in the result dom.
I think what you want are slots. (See https://v2.vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots).
The code of your component would look like this:
<div>
<slot></slot>
<myChildComponent/>
</div>
A runnable example
Below we have an alert-box component that displays an error message inside a <div> styled with a red background. This is how we use it:
<alert-box> This email address is already in use. </alert-box>
And it generates HTML that looks like this:
<div>
<strong>Error!</strong>
This email address is already in use.
</div>
See it in action:
Vue.component('alert-box', {
template: `
<div class="demo-alert-box" style="background-color: red; color: white;">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<alert-box> This email address is already in use. </alert-box>
<p> I'm a normal paragraph. </p>
</div>

VueJS - usage of v-html attribute not working

In my case, I am replacing a link on top of paragraph element using v-html
Please find the code snippet as follows
<p v-html="websiteHTML"></p>
where websiteHTML contains: <a v-bind:href="google.com/">Google</a>
The <p> tag is being rendered with Google but doesn't have hyperlink to navigate to https://www.google.com/
Could you please help in finding the error?
The HTML string you include in your variable should just be HTML, not Vue template code. When you tried including vue template directives, the framework wrote the anchor tag into the DOM with the literal attribute "v-bind:href" instead of the desired "href":
new Vue({
el: '#app',
data: {
websiteHTMLNo: '<a v-bind:href="https://google.com/">Google</a>', // <-- won't work
websiteHTMLYes: 'Google' // <-- do this instead
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-html="websiteHTMLNo"></p>
<p v-html="websiteHTMLYes"></p>
</div>
(If you actually do need to inject template code instead of plain HTML, you need to use Vue.compile instead of v-html to parse it.)
if you want to pass a var inside your link.
const url: string = 'https://google.fr/'
const html: string = `Google`
<p v-html="html"></p>

Why does my Vue component require :key?

I have a small Vue.js component which displays a favorite star icon. Clicking on the icon favorites/unfavorites the element. So far I have only implemented the UI part, which looks like this:
<template>
<div :key="favorite">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="favorite" class="text-warning fas fa-star"></i>
<i v-show="!favorite" class="text-warning far fa-star"></i>
</a>
</div>
</template>
<script>
export default {
data() {
return {
favorite: true,
}
},
mounted() {
},
methods: {
toggleFavorite() {
this.favorite = !this.favorite
}
},
props: ['team-id'],
}
</script>
<style scoped>
</style>
As you can see, the logic is pretty simple.
This works well, but one thing that bothers me is that, if I remove the :key property from my template, the icon is not updated when I click on it (even though I have checked that the underlying property is indeed updated correctly). Adding :key makes it work, I imagine because it forces Vue.js to completely re-render the component when favorite is updated.
Why is this happening? I'm fairly new to the world of JS frameworks, so forgive any obvious stuff I might be missing. I did some research online but couldn't find an explanation. I just want to make sure I'm doing things the right way and not merely hacking around the issue here.
Vue patches with the virtual DOM whenever it is necessary. That is, whenever vue detects the changes on the DOM, it patches them for faster performance. And patching in the DOM will not change the icon or image. You need to replace the DOM instead.
Thus, vue provides the way for us whenever we need to change the DOM by replacing method, we can use :key binding.
So, :key binding can be used to force replacement of an element/component instead of reusing it.
The following whole html div will be replaced whenever there is change in favorite data as we're :key binding on it:
<div :key="favorite">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="favorite" class="text-warning fas fa-star"></i>
<i v-show="!favorite" class="text-warning far fa-star"></i>
</a>
</div>
This is why vue forcefully allows us to use :key binding inside a loop as there's need of replacing the elements inside the loop whenever it detects the changes in the data. This is made compulsory from 2.2.0+ and ESLint also have implemented this feature so that if you miss :key binding inside the loop, then you'll see the error on that line when you use editor that supports eslint, so that you can fix the error.
Just an opinion, the strict requirement of the :key binding should be removed from the vue as we might want a loop of predefined data and don't want to change the DOM but we still use the v-for loop for listing bigger data. But it might be rare case though.
Read carefully on the documentation for :key binding and then you'll have an idea.
The :key binding can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
Use :key binding to replace the DOM. Remember it slower the performance as it replace the whole DOM that is bound to the element.
Don't use :key binding when you don't want to replace the DOM or
you think there's no data changes detection required. This will
allow vue to perform better without :key binding.
Its seems to be a general issue of FontAwesome CSS regardless the framework.
There is an issue on github and here the same issue with react https://github.com/FortAwesome/Font-Awesome/issues/11967
To prove that, here is a simplified version of the same example but using bootstrap icons
new Vue({
el: '#app',
data() {
return {
fav: true
}
}
});
<script
src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"
></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<div id="app">
<div>
<a v-on:click="fav = !fav" style="cursor: pointer">
<i v-show="fav" class="glyphicon glyphicon-star"></i>
<i v-show="!fav" class="glyphicon glyphicon-star-empty"></i>
</a>
</div>
</div>
You shouldn't need the :key, it's only necessary in v-for loops. I would suggest you remove it and replace your v-show with a v-if and v-else directive.
<i v-if="favorite" class="text-warning fas fa-star"></i>
<i v-else class="text-warning far fa-star"></i>
v-if removes and addes the section to the DOM whereas v-show just hides it so this way well resolve your issue
Ok I think the problem here is that you're changing your root data object. To preserve reactivity, you shouldn't change the root data object after you've instantiated Vue.
Here is your code in a simple Vue. I didn't need :key to make it work. I would keep :key for inside loops.
markup
<div id="vueRoot">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="store.favorite" class="text-warning fas fa-star">Fav</i>
<i v-show="!store.favorite" class="text-warning far fa-star">Not fav</i>
</a>
</div>
code
vm = new Vue({
el : "#vueRoot",
data() {
return { store :{
favorite: true
}}
},
mounted() {
},
methods: {
toggleFavorite() {
this.store.favorite = !this.store.favorite
}
}
}
);
This is a working example with minimal changes. From what you've showed us, you should just have <i> element, then do what you want with a dynamic class list, like...
<i :class="['text-warning','fa-star',store.favorite?'fas':'far']"></i>

Vue.js: initialize the data object with data from the page or from v-text directive

So I'm just starting to play with Vue.js and I would like to know if there is a nice way to initialize the data object with html tags content from the page.
Basically I have a page that displays information and I would like to turn it into a tiny Vue application to avoid having a separated edit page.
So I added the form with 2 way binding which submits the values via ajax.
The thing is I really want to avoid Flash Of Uncompiled Content, but then I feel like I have to duplicate all my data, once in the html tag, once in the data object.
<div id="app">
<span v-text="prop1">This is filled by the backend</span>
<input v-model="prop1" type="text" v-cloak />
</div>
<script>
new Vue({
el: "#app",
data: {prop1: "This is filled by the backend..again"} // << Can I avoid this?
});
</script>
Could I tell Vue to get the data from the html tags since it's already there?
Thanks!
You are looking for props
Your html element would look something like
<custom-element dataToPass="dataFromHtml"></custom-element>.
Then you can access it in your vue instance via this.dataToPass.
Have a read through the documentation, there is a difference if you pass a string or an expression (from another vue instance for example).
In your example:
<div id="app">
<span prop1="{ backendVariable}" v-text="prop1"></span>
<input v-model="prop1" type="text" v-cloak />
</div>
<script>
new Vue({
el: "#app",
props: ['prop1']
});
</script>
```