Reputation: 6045
I've read the documentation and I'm not sure if what I'm trying to achieve is actually doable.
What I'm after is the component's property that contains html tags, which obviously produce invalid html - hence I was wondering if there is a way of passing html into the component and sending it further with emitted event.
The most suitable way would be to have named slot
, but I don't think I can associate content of a named slot with internal property.
The component I work on is a simple confirmation dialog. The trigger component would wrap two slots - one for label and the other for message I'd like to dispatch with the event to the dialog component.
My trigger component looks like this at the moment:
<template>
<a :class="cssClass" @click="clicked()">
<slot></slot>
</a>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true
},
route: {
type: String,
required: true
},
message: {
type: String,
required: true
},
cssClass: {
type: String,
required: false
}
},
mounted() {
window.EventHandler.listen('confirm-dialog-' + this.id + '-called', () => {
window.location.reload(true);
});
},
methods: {
clicked() {
window.EventHandler.fire('top-confirm', {
id: 'confirm-dialog-' + this.id,
message: this.message,
url: this.route
});
}
}
};
</script>
and when added to the html:
<call-dialog
route="{{ route('subscriber.destroy', $subscriber->id) }}"
css-class="alert"
id="{{ $subscriber->id }}"
message="Are you sure you wish to remove '{{ $subscriber->email }}'?<br />There is no undo!"
>
<i class="fa fa-trash"></i> Remove
</call-dialog>
As you can see, at the moment I'm passing the message using message
prop, but this particular one contains <br />
tag, which produces invalid html.
Any idea how to tackle this?
NOTE
I think I've found the way of doing it with named slots:
<template>
<a :class="cssClass" @click.prevent="clicked()">
<slot name="label"></slot>
</a>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true
},
route: {
type: String,
required: true
},
cssClass: {
type: String,
required: false
}
},
mounted() {
window.EventHandler.listen('confirm-dialog-' + this.id + '-called', () => {
window.location.reload(true);
});
},
methods: {
message() {
return this.$slots.message[0].context.message;
},
clicked() {
window.EventHandler.fire('top-confirm', {
id: 'confirm-dialog-' + this.id,
message: this.message(),
url: this.route
});
}
}
};
</script>
And now simply passing and additional named slot slot="message"
<call-dialog
route="{{ route('subscriber.destroy', $subscriber->id) }}"
css-class="alert"
id="{{ $subscriber->id }}"
>
<span slot="label"><i class="fa fa-trash"></i></span>
<span slot="message">Are you sure you wish to remove '{{ $subscriber->email }}'?<br />There is no undo!</span>
</call-dialog>
Upvotes: 1
Views: 1861
Reputation: 23968
You should use v-html
inside of the call-dialog
component to insert the raw HTML from the message prop into an element.
<div v-html="message"></div>
https://v2.vuejs.org/v2/api/#v-html
Upvotes: 0
Reputation: 5241
I've had to do something similar for a custom button component.
Vue.prototype.$getSlotText = function( name = 'default' ) {
const htmlElements = this.$slots[name];
if(!htmlElements || !htmlElements.length) return '';
return htmlElements.reduce((fullText, {tag, text}) => {
return fullText + (tag === 'br' ? '\n' : text || '');
}, '');
}
Essentially, this would access the HTML-Elements on the "default" slot (by default).
''
blank string.text
property if found, or \n
if it's a <br/>
tag.Note: The .reduce(...)
method is used for the iteration / concatenation. Alternatively a .map(...)
call with a similar arrow-function, followed by a .join('')
would probably result the same thing.
Upvotes: 0