How to use script in vue - vue.js

I am not very good at English, I will describe the problem as clearly as possible.
<template>
<div class="Index">
<div
class="content"
v-html="content"
></div>
</div>
</template>
export default {
data() {
return {
content: '',
}
},
mounted() {
// This data is returned by axios,This is rich text with lots of tags, including script
this.content="<p>1111</p><div>222<div><script>alert() //There is a lot of code here.</script>"
}
}
The console will report this error:
Syntax Error: SyntaxError: /Users/sinx/development/cxz/src/views/Index.vue: Unterminated template (33:19)
Now, the problem is how to insert the data returned by axios into vue using v-html. Thank you all.
The initial judgment seems to be due to the reason of <script>

Make sure to include your js code inside scripts tags, like below;
<script> include </script>
That should fix your problem.

you should put all your JS in the script tag. The basic structure of a Vue app is like this.
<template>
//Your html here
</template>
<script>
export default {
data(){
return {
content : ""
}
},
mounted(){
axios.get(url).then(response => {
this.content = response.data //depends on the structure of response
}).then(error => {
console.log(error);
});
}
}
<script>
Hope this helps :)

Find the problem, if you want to render rich text, especially rich text with a srcipt tag, the tag needs to be escaped.
But,Although v-html can render tags, but can't execute the contents of the script, if I want to know how to execute the contents of the script, I have a way.

Use axios...
axios.get function uses a Promise.
When the API returns data successfully, the code within the then block is executed.
mounted() {
axios.get(url).then(response => {
this.results = response.data
})
}
Here is the link
Using Axios to Consume APIs
from official site.

Related

vue 2 [GSI_LOGGER]: The value of 'callback' is not a function. Configuration ignored

I'm trying to put a google sign in button inside my Vue2 project, so I tried to follow the instructions here https://developers.google.com/identity/gsi/web/guides/display-button#html
So I put this code below into my Hello.vue component
<template>
<section>
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
data-callback=myCallbackFunction
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left">
</div>
</section>
</template>
<script>
export default {
methods: {
myCallbackFunction(){
}
}
}
</script>
and when I reloaded my page/component, it will display the error [GSI_LOGGER]: The value of 'callback' is not a function. Configuration ignored.
I think the problem is data-callback couldn't find or recognize myCallbackFunction which I already declared under methods. I've also tried to put myCallbackFunction under computed instead, but it still return the same error. So is there any way I can make this work?
Ok, I think I got itโ€”but I switched from using the HTML documentation to the JavaScript documentation, since VueJS works better with this.
Still, I don't know if mounted is the best option, but it's at least working as intended.
Just use the callback function created at methods, and that's it.
mounted: function () {
google.accounts.id.initialize({
client_id:
'xxxxxxx.apps.googleusercontent.com',
callback: this.handleCredentialResponse,
})
google.accounts.id.prompt()}
working for me in Vue 2
<template>
<div>
<div id="signin_button"></div>
</div>
</template>
<script>
export default {
components: {
},
methods: {
handleCredentialResponse(response) {
console.log(response);
}
},
mounted: function () {
let googleScript = document.createElement('script');
googleScript.src = 'https://accounts.google.com/gsi/client';
document.head.appendChild(googleScript);
window.addEventListener('load', () => {
console.log(window.google);
window.google.accounts.id.initialize({
client_id: "xxxxxxx.apps.googleusercontent.com",
callback: this.handleCredentialResponse
});
window.google.accounts.id.renderButton(
document.getElementById("signin_button"),
{ theme: "outline", size: "large" } // customization attributes
);
})
}
}
</script>
use globalThis.yourcallbackfunction

Pass Vue js search filter functionality through single file components with EventBus

I have the following components:
/components/SearchBlogs.vue Search component to filter on blog.title and blog.description.
/components/BlogList.vue Here I list all the Blog items.
SearchBlogs.vue
<template>
<div>
<input type="text" v-model="search" #change="emitSearchValue" placeholder="search blog">
</div>
</template>
<script>
import { EventBus } from '../event-bus.js'
export default {
name: 'SearchBlogs',
data: () => {
return {
search: ''
}
},
methods: {
emitSearchValue() {
EventBus.$emit('search-value', 'this.search')
}
}
}
</script>
BlogList.vue
<template>
<div>
<div v-for="blog in filteredBlogs" :key="blog">
<BlogListItem :blog="blog" />
</div>
</div>
</template>
<script>
import BlogListItem from './BlogListItem'
import { EventBus } from '../event-bus.js'
export default {
name: 'BlogList',
components: {
BlogListItem,
},
data: () => {
return {
blogs: [],
searchvalue: ''
}
},
computed: {
filteredBlogs() {
return this.blogs.filter(blog =>
blog.name.toLowerCase().includes(
this.searchvalue.toLowerCase()
)
)
}
},
created() {
fetch('http://localhost:3000/blogs')
.then(response => {
return response.json();
})
.then(data => {
this.blogs = data;
}),
EventBus.$on('search-value', (search) => {
this.searchvalue = value;
})
}
}
</script>
In another page component Blogs I register both components:
<template>
<div>
<h1>Blog</h1>
<TheSidebar>
<SearchBlogs />
</TheSidebar>
<BlogList/>
</div>
</template>
Can anybody see what's missing here? I want, as soon as the user types something in the search input (from the SearchBlogs.vue component), it start filtering and updating the list.
Look at my solution condesandbox
Here is an explanation:
You don't need to use EventBus. You can communicate with Search Component by v-model, using prop value and emiting updated value from the Input.
Then your Main (List) Component is responsible for all the logic.
It keeps the state of a Search
It keeps the items and filtered Items
Thanks to that your Search Component is very clear and has no data, that means it has very little responsibility.
Please ask questions if I can add something to help you understand ๐Ÿ˜‰
UPDATE:
EventBus is a great addition in some cases. Your case is simple enough, there is no need to add it. Right now your architecture is "over engineered".
When you have added listener on EventBus, on created:hookyou should always remove it while Component is being destroyed. Otherwise you can encounter a trouble with double calling function etc. This is very hard to debug, tryst me I'he been there ๐Ÿ˜‰
Going with my suggestion gives you comfort of "no-need-to-remember-about-this" because Vue is doing it for you.
Hope that help.
Couple of issues but essentially the computed prop filteredData will look like:
computed: {
filteredData() {
return this.experiences.filter(
el => el.category.indexOf(this.search) > -1
);
}
}
Also, used quotes around 'this.search' when passing its value back which made it a string.
Fixed sandbox
https://codesandbox.io/s/reverent-lamarr-is8jz

Nuxt render function for a string of HTML that contains Vue components

I'm trying to solve this for Nuxt
Codesandbox of a WIP not working: https://codesandbox.io/s/zw26v3940m
OK, so I have WordPress as a CMS, and it's outputting a bunch of HTML. A sample of the HTML looks like this:
'<h2>A heading tag</h2>
<site-banner image="{}" id="123">Slot text here</site-banner>
<p>some text</p>'
Notice that it contains a Vue component <site-banner> that has some props on it (the image prop is a JSON object I left out for brevity). That component is registered globally.
I have a component that we wrote, called <wp-content> that works great in Vue, but doesn't work in Nuxt. Note the two render functions, one is for Vue the other is for Nuxt (obviously this is for examples sake, I wouldn't use both).
export default {
props: {
html: {
type: String,
default: ""
}
},
render(h, context) {
// Worked great in Vue
return h({ template: this.html })
}
render(createElement, context) {
// Kind of works in Nuxt, but doesn't render Vue components at all
return createElement("div", { domProps: { innerHTML: this.html } })
}
}
So the last render function works in Nuxt except it won't actually render the Vue components in this.html, it just puts them on the page as HTML.
So how do I do this in Nuxt? I want to take a string of HTML from the server, and render it on the page, and turn any registered Vue components into proper full-blown Vue components. Basically a little "VueifyThis(html)" factory.
This was what worked and was the cleanest, thanks to Jonas Galvez from the Nuxt team via oTechie.
export default {
props: {
html: {
type: String,
default: ""
}
},
render(h) {
return h({
template: `<div>${this.html}</div>`
});
}
};
Then in your nuxt.config.js file:
build: {
extend(config, ctx) {
// Include the compiler version of Vue so that <component-name> works
config.resolve.alias["vue$"] = "vue/dist/vue.esm.js"
}
}
And if you use the v-html directive to render the html?
like:
<div v-html="html"></div>
I think it will do the job.
Here's a solution on codesandbox: https://codesandbox.io/s/wpcontent-j43sp
The main point is to wrap the dynamic component in a <div> (so an HTML tag) in the dynamicComponent() template, as it can only have one root element, and as it comes from Wordpress the source string itself can have any number of top level elements.
And the WpContent component had to be imported.
This is how I did it with Nuxt 3 :
<script setup lang="ts">
import { h } from 'vue';
const props = defineProps<{
class: string;
HTML: string
}>();
const VNode = () => h('div', { class: props.class, innerHTML: props.HTML })
</script>
<template>
<VNode />
</template>
There was not need to update nuxt.config.ts.
Hopefully it will help some of you.
I made some changes to your codesandbox. seems work now https://codesandbox.io/s/q9wl8ry6q9
Things I changed that didn't work:
template can only has one single root element in current version of Vue
v-bind only accept variables but you pass in a string.

Vue: render <script> tag inside a variable (data string)

I'm new to Vue.js
I want to render a script tag inside a variable (data string).
I tried to us a v-html directive to do so, but it doesn't work Nothing is rendered
Any way I can achieve this?
I'd place a v-if directive on the script tag and put the content of it in a variable.
<script v-if="script">
{{script}}
</scrip>
If I understand you correctly, my answer is:
<template>
<div>
{{ strWithScriptTag }}
</div>
</template>
<script>
export default {
name: 'Example',
methods: {
htmlDecode(input) {
const e = document.createElement('div')
e.innerHTML = input
return e.childNodes[0].nodeValue
},
},
computed: {
strWithScriptTag() {
const scriptStr = '<script>https://some.domain.namet</script>'
return this.htmlDecode(scriptStr)
}
},
}
</script>
I think that by safety vue is escaping your <script> automatically and there is no way to avoid this.
Anyway, one thing you can do is eval(this.property) on created() lifecycle hook.
data: {
script: 'alert("this alert will be shown when the component is created")'
},
created() {
eval(this.script)
}
Use it with caution, as stated in vue js docs, this may open XSS attacks in your app

Vue ElementUI MessageBox with HTML Message?

I'm trying to use the MessageBox of ElementUI in my Vue app to show dynamic HTML content. The content is coming from JSON data as an HTML string, that essentially needs to be parsed into HTML. Normally, you could do this using the directive v-html, but there's no such apparent option with the MessageBox component.
Apparently, you can render HTML in the message, but you have to use the VNode method(?). The examples I've seen show how to do this if you write the HTML in the script, but as I'm pulling preformatted HTML as a string from JSON data I don't see how I can use this to my advantage.
I might just have to switch to another modal component, but as I'm using several of the ElementUI components already I'd rather keep it all part of the same system.
Here's my script:
<template>
<div>
<el-button
v-if="body"
type="text"
#click="openModal"
>More Info
</el-button>
</div>
</template>
<script>
export default {
methods: {
openModal() {
const h = this.$createElement;
this.$msgbox( {
title: this.title,
message: this.body,
confirmButtonText: 'OK',
closeOnClickModal: true,
showCancelButton: false
} );
}
},
props: {
body: String, // The MessageBox message
title: String // The MessageBox title
}
}
</script>
One potential solution is to compile the HTML into a render function and pass that to Vue's createElement.
message: this.$createElement(Vue.compile(this.body))
Here is an example based of the documentation.