Vuejs how can i attach html with v-html - javascript

As we know, we can have any html tag in vuejs via v-html, here in mounted() lifecycle function i want to make a simple link and attach it in to p tag. for example:
<template>
<p v-html="caption"></p>
</template>
<script>
export default {
name: "emulator",
props: {
caption: {
type: String,
required: false
}
},
mounted() {
this.$emit('caption', this.$el.innerText.replace(
/#([\p{L}\p{N}_-]+)/u,
'$&'
))
}
}
</script>
the this.$emit work fine but i don't have any link into p tag when i try to type for example:
hi #vuejs
it should be: <p>hi #vuejs</p>
replace method work fine too and we can test it from this link

I don't know if $emit in this component is necessary but the following works:
<template>
<p v-html="link"></p>
</template>
<script>
export default {
name: "emulator",
props: {
caption: {
type: String,
default: '#john'
}
},
computed: {
link(){
const anchorTag = this.caption.replace(
/#([\p{L}\p{N}_-]+)/u,
'$&'
)
this.$emit(anchorTag) // <--- remove this line if you don't need the HTML tag in your parent
return anchorTag
}
}
}
</script>

Related

Conflicting event handlers in vue.js

I have a page that contains two buttons. I want to use these two buttons to navigate to different pages.
If I just include one of the buttons it works fine. if I include both only one works (as I will show with debug statements if the second button is there, the event handler of the first button is not triggered).
My guess is, that the second button is somehow conflicting with the first button, but I do not know why and how to fix it.
These are some code snippets:
Backbutton.vue
<template>
<div>
<button #click.stop="navigate()"/>
</div>
</template>
<script>
export default {
name: 'BackButton',
methods: {
navigate(){
console.log("B");
}
}
}
</script>
Finishbutton.vue
<template>
<div :style="visible ? { 'display': 'inline-flex' } : { 'display': 'none' }">
<button #click.stop="navigate()"/>
</div>
</template>
<script>
export default {
name: 'FinishButton',
props : {
visible: Boolean
},
methods: {
navigate(){
console.log("F");
}
}
}
</script>
Page.vue
<template>
<BackButton/>
<FinishButton :visible=ready></FinishButton>
</template>
<script>
import BackButton from "../components/BackButton.vue"
import FinishButton from "../components/FinishButton.vue"
export default {
name: 'Page',
components: {
BackButton,
FinishButton
},
data() {
return {
ready: true
}
},
}
</script>
If ready on the page is false (and therefore the finish-button is not visible), a click on the backbutton prints "B". If ready is true, the finishbutton prints "F", but clicking on the backbutton does not yield any output.
Help is greatly appreciated.
There are some minor issues here and there with your code, but the following works well (not sure where exactly this is coming from tho).
Page.vue
<template>
<div>
<BackButton></BackButton>
<FinishButton :visible="ready"></FinishButton>
</div>
</template>
<script>
import BackButton from '../components/BackButton.vue'
import FinishButton from '../components/FinishButton.vue'
export default {
name: 'Page',
components: {
BackButton,
FinishButton,
},
data() {
return {
ready: true,
}
},
}
</script>
BackButton.vue
<template>
<div>
<button #click.stop="navigate">back</button>
</div>
</template>
<script>
export default {
name: 'BackButton',
methods: {
navigate() {
console.log('B')
},
},
}
</script>
FinishButton.vue
<template>
<div :style="visible ? { display: 'inline-flex' } : { display: 'none' }">
<button #click.stop="navigate">finish</button>
</div>
</template>
<script>
export default {
name: 'FinishButton',
props: {
visible: Boolean,
},
methods: {
navigate() {
console.log('F')
},
},
}
</script>
Or at least, I cannot reproduce your issue with the provided snippet.

Adding dynamic async component by name in variable

De idea is not to program the name, but to pass the name as a property.
So i can have a base vue component and i tell what vue component to load in there dynamicly by giving name and location.
Which works perfect when i type the string (set the name) but not when i put the name in a string then it says module not found.
so if I import('./yyy.vue') it works but if. but if I import(var_that_is_the_name) it says not found. i think it has something to do with laravel mix or stringparsing.
<template>
<div class="xxx" ref="xxx">
<component :is="dynamicComponent"></component>
<div class="right">
right {{ this.DvueLocation }}
</div>
</div>
</template>
<script>
import { defineAsyncComponent } from '#vue/runtime-core'
export default {
name: 'xxx',
components: {
},
props: {
DvueLocation: {
type: String,
default: "./yyy.vue",
},
DvueName: {
type: String,
default: "yyy"
}
},
data: function () {
return {
isMounted: false,
}
},
computed: {
dynamicComponent() {
console.log(this.DvueLocation);
return defineAsyncComponent(() => import('./yyy.vue'))
return defineAsyncComponent(() => import(this.DvueLocation))
}
},
}
</script>
the yyy.vue is a simple component that prints yyy.
first line works, second with not cold not add // for it and save.
i tried putting "'" + around it en lots more
while its exactly the same if i type it.
both the string printed and on console of browser gives the right value
Something to do with string parsing.
return defineAsyncComponent(() => import('./' + this.DvueName))
This works for me now!

Active events in vue instance encapsulated inside shadowDOM

I'm searching to trigger event from a vue instance which is encapsulated inside a shadowDOM.
For the moment, my code create a new Vue instance inside a shadowDOM and print the template without the style (because nuxt load the style inside the base vue instance).
I can call methods of each instance
But, when i'm trying to add an event / listener on my component, it doesn't work.
DOM.vue
<template>
<section ref='shadow'></section>
</template>
<script>
import bloc from '#/components/bloc.vue'
import Vue from 'vue'
export default {
data() {
return {
vm: {},
shadowRoot: {},
isShadowReady: false
}
},
watch: {
isShadowReady: function() {
let self = this
this.vm = new Vue({
el: self.shadowRoot,
components: { bloc },
template: "<bloc #click='listenClick'/>",
methods: {
listenClick() { console.log("I clicked !") },
callChild() { console.log("I'm the child !") }
},
created() {
this.callChild()
self.callParent()
}
})
}
},
mounted() {
const shadowDOM = this.$refs.shadow.attachShadow({ mode: 'open' })
this.shadowRoot = document.createElement('main')
shadowDOM.appendChild(this.shadowRoot)
this.isShadowReady = true
},
methods: {
callParent() {
console.log("I'am the parent")
},
}
}
</script>
bloc.vue
<template>
<div>
<p v-for='word in words' style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
}
}
</script>
Any idea ?
Thank you
Hi so after i watched at your code i think it's better to use the $emit to pass event from the child to the parent and i think it's more optimized then a #click.native
template: "<bloc #someevent='listenClick'/>",
<template>
<div #click="listenClickChild">
<p v-for='(word, idx) in words' :key="idx" style='text-align: center; background: green;'>
{{ word }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
words: [1,2,3,4,5,6]
}
},
methods: {
listenClickChild() {
this.$emit('someevent', 'someValue')
}
}
}
</script>
bloc #click.native='listenClick'/>

CSS of component balise doesn't load inside spefic component

I've a problem to load the css of my bloc component.
The webpage component allow to create an iframe and set some content inside easily.
It load correctly the template and script tag but not the css (it doesn't load).
Sometime it works, most of the time, it didn't.
I was thinking that it was a problem with the loading of the component but no.
If I load the component before or after the render of my "webpage" component : it don't load.
I've try with the auto import to true and after to false, but it solve nothing.
I have 2 components : webpage and bloc.
bloc.vue
<template>
<div class="bloc">
<p>Le texte</p>
</div>
</template>
<style>
.bloc {
background-color: blue;
padding: 20px;
}
</style>
webpage.vue
<script>
import Vue from "vue";
export default {
props: {
css: {
type: String,
required: false,
},
},
data() {
return {
load: false,
};
},
render(h) {
return h("iframe", {
on: { load: this.renderChildren },
});
},
beforeUpdate() {
//freezing to prevent unnessessary Reactifiation of vNodes
this.iApp.children = Object.freeze(this.$slots.default);
},
mounted() {
if (!this.load) this.renderChildren();
},
methods: {
// https://forum.vuejs.org/t/render-inside-iframe/6419/12
renderChildren() {
this.load = true;
const children = this.$slots.default;
const head = this.$el.contentDocument.head;
const body = this.$el.contentDocument.body;
let style = this.$el.contentDocument.createElement("style");
style.textContent += this.$props.css;
head.appendChild(style);
const iApp = new Vue({
name: "iApp",
// freezing to prevent unnessessary Reactifiation of vNodes
data: { children: Object.freeze(children) },
render(h) {
return h("body", this.children);
},
});
this.iApp = iApp; // cache instance for later updates
this.iApp.$mount(body); // mount into iframe
},
},
};
</script>
app.vue
<template>
<Webpage>
<component :is="name"></component>
</Webpage>
</template>
<script>
import bloc from "#/components/Bloc";
import Webpage from "#/components/Webpage";
export default {
components: {
bloc,
Webpage,
},
computed: {
name() {
return "bloc";
},
},
};
</script>
Do you have an idea where this might come from ?
The codesanbox : https://codesandbox.io/s/error-style-component-import-1t1hs?file=/pages/index.vue
Thank you.
Probably Webpage component overrides it.
Try moving your style to index and <Webpage class="bloc">

Building a form with Vuejs. Pass data from children to parent

I'm trying to build a form using "v-for" for input component and then generate a pdf file with PDFMake using data from inputs. But I didn't know how to pass the data from input component back to parent.
I read a lot of topics, but can't find a way to do this.
Here is short code without additional inputs, checkboxes etc. I plan to use around 15 inputs with different parameters to generate final PDF. Some of parameters also will be used to change final data depending of conditional statements.
Everything is work fine if code in one file, without loop and components. But not now.
Here is parent:
<template lang="pug">
.form
Input(v-for="data in form.client_info" v-bind:key="data.id" v-bind:data="data")
button(#click="pdfgen") Download PDF
</template>
<script>
export default {
components: {
Input: () => import('#/components/items/form/input')
},
data() {
return {
client_name: '',
client_email: '',
form: {
client_info: [
{id:'client_name', title:'Name'},
{id:'client_email', title: 'Email'},
{id:'foo', title: 'foo'}
],
}
}
},
methods: {
pdfgen: function () {
var pdfMake = require('pdfmake/build/pdfmake.js')
if (pdfMake.vfs == undefined) {
var pdfFonts = require('pdfmake/build/vfs_fonts.js')
pdfMake.vfs = pdfFonts.pdfMake.vfs;
}
if (this.foo) {
var foo = [
'Foo: ' + this.foo
];
} else {
foo = ''
]
}
var docDefinition = {
content: [
'Name: ' + this.client_name,
'Email: ' + this.client_email,
'\n',
foo
]
}
pdfMake.createPdf(docDefinition).download('Demo.pdf');
}
}
}
</script>
Here is a children (Input component):
<template lang="pug">
label.form_item
span.form_item_title {{ data.title }}
input.form_item_input(:v-model="data.id" type="text")
</template>
<script>
export default {
props: ['data']
}
</script>
Any ideas how to make it work?
You'll want to use a method that vue has build-in named $emit().
Before going into how to do that, a quick explanation. Because vue attempts to make data flow one-directional there is not a super quick way to just pass data back to a parent. What Vue proposes instead is to pass a method to the child component that, when called, will 'emit' the value it changed to it's parent and the parent can then do what it wants with that value.
So, in your parent component you'll want to add a method that will handle a change when the child emits. This could look something like:
onChildValueChanged(value){ this.someValue = value }
The value we passed to the function will be coming from our child component. We will need to define in our child component what this function should do. In your child component you could have a function that looks like so:
emitValueChange(event){ this.$emit('childFunctionCall', this.someChildValue) }
Next we need to tie those two functions together by adding an attribute on our child template. In this example that might look like:
<Child :parentData="someData" v-on:childFunctionCall="onChildValueChanged"></Child>
What that above template is doing is saying that when the function on:childFunctionCall is 'emited' then our function in the parent scope should fire.
Finally, in the child template we just need to add some event that calls out emiter. That could look like:
<button v-on:click="emitToParent">This is a button</button>
So when our button is clicked, the emiter is called. This triggers the function in our child component named 'emitToParent' which in turn calls the function we passed to our child component.
You'll have to tailor your use case to match the exam
I found a solution using Vuex.
So now my components look like this.
Here is parent:
<template lang="pug">
.form
Input(v-for="data in formClient" v-bind:key="data.id" v-bind:data="data")
button(#click="pdfgen") Download PDF
</template>
<script>
export default {
components: {
Input: () => import('#/components/items/form/input'),
store: () => import('#/store'),
},
computed: {
formClient() { return this.$store.getters.client }
}
}
</script>
Here is a children (Input component):
<template lang="pug">
label.form_item
span.form_item_title {{ data.title }}
input.form_item_input(v-model="data.value" :type="data.input_type")
</template>
<script>
export default {
props: ['data'],
computed: {
form: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
}
</script>
Here is a store module:
<script>
export default {
actions: {},
mutations: {},
state: {
form: {
client: [
{id:'client_name', title:'Name', value: ''},
{id:'client_email', title: 'Email', value: ''},
{id:'foo', title: 'foo', value: ''}
]
}
},
getters: {
client: state => {
return state.form.client;
}
}
}
</script>
Now I can read updated data from store directly from PDFMake function.

Categories

Resources