Im new to Vuejs and I get the error unknown custom element -
how do i register a custom element - b-alert. I think this element is from bootstrapVue.
<template>
<div>
<b-alert show>Default Alert</b-alert>
</div>
</template>
<script>
export default {
data () {
return {
dismissSecs: 10,
dismissCountDown: 0,
showDismissibleAlert: false
}
},
methods: {
countDownChanged (dismissCountDown) {
this.dismissCountDown = dismissCountDown
},
showAlert () {
this.dismissCountDown = this.dismissSecs
}
},
}
You will have to register the component as in Component registration
import { Alert } from 'bootstrap-vue/es/components';
components: { BAlert }
Since all html tags are turned into dashed by camelcase BAlert = '<b-alert>'
Alertenativly you can also use
components: { 'b-alert': BAlert }
Related
I am using vue-multiselect to display a multi select dropdown list on UI in vue js.
It is present in component section of vue extension by browser, CSS is also present in inspect element, but multiselect dropdown is not getting rendered on the UI.
Below is my html and js code, and there is no error in console.
HTML code
<multiselect
v-model="pincodeone"
tag-placeholder="Add this as new tag"
tag-position="bottom"
placeholder="Enter the pincode(s)"
label="name"
:options="options"
:multiple="true"
:taggable="true"
#tag="PincodeTag"
#remove="RemoveTag"
></multiselect>
Js code
import Multiselect from 'vue-multiselect';
import 'vue-multiselect/dist/vue-multiselect.min.css';
export default {
name: "test",
components: { Multiselect },
props: [],
data() {
return { options: [], pincodeone: [], pincodetwo: [] };
},
methods: {
PincodeTag(newTag) {
const tag = { name: newTag };
this.options.push(tag);
this.pincodeone.push(tag);
this.pincodetwo.push(newTag);
},
RemoveTag({ name }) {
const index = this.pincodetwo.indexOf(name);
if (index > -1) {
this.pincodetwo.splice(index, 1);
this.options.splice(index, 1);
}
},
}; }}}}
package.json file
"vue-multiselect": "^2.1.6",
This is the error I get in console running your code
[Vue warn]: Property or method "RemoveTag" is not defined on the instance but referenced during render.
The problem is that your RemoveTag method is outside of the methods object. Your entire methods object should be like this:
methods: {
PincodeTag(newTag) {
const tag = { name: newTag };
this.options.push(tag);
this.pincodeone.push(tag);
this.pincodetwo.push(newTag);
},
RemoveTag({ name }) {
const index = this.pincodetwo.indexOf(name);
if (index > -1) {
this.pincodetwo.splice(index, 1);
}
}
}
I have a modal component and I'm writing the story for it. It looks something like this:
import { Story, Meta } from '#storybook/html';
export default {
title: 'Components/Modal',
argTypes: {
open: {
name: 'Opened',
control: 'boolean'
},
},
args: {
open: false,
}
} as Meta;
const Template: Story = (args) => {
return `
<my-modal open="${args.open}">
Some example content inside the modal
</my-modal>
`;
};
export const Modal: Story = Template.bind({});
I have the arg open on the controls and I can change its value to true and the modal shows. But I would like the story to have a button and when it's clicked, the modal shows.
I can't find a way to do this in the current version of Storybook for web components.
I've seen there are some hooks available for React (import { useArgs } from '#storybook/api';) that allows you to change the arguments value dynamically but I can't see how to do this for web components?
Any helps will be highly appreciated.
Just add that button to the template:
import { Story, Meta } from '#storybook/html';
export default {
title: 'Components/Modal',
argTypes: {
open: {
name: 'Opened',
control: 'boolean'
},
},
args: {
open: false,
}
} as Meta;
const Template: Story = (args) => {
return `
<button
type="button"
onclick="this.nextElementSibling.open = !this.nextElementSibling.open">
Toggle Modal
</button>
<my-modal .open=${args.open}>
Some example content inside the modal
</my-modal>
`;
};
export const Modal: Story = Template.bind({});
Also, for boolean attributes - if implemented properly -
you should work with the property (prefix it in the template with a .) rather than the attribute.
Doing that with all native code isn't rocket science...
<my-dialog id="DIALOG" open>
Hello *Native* Web Components world!
</my-dialog>
<button onclick="DIALOG.open()">OPEN</button>
<script>
customElements.define("my-dialog", class extends HTMLElement {
static get observedAttributes() {
return ["open"];
}
constructor() {
super() // sets and returns 'this'
.attachShadow({mode:"open"}) // sets and return this.shadowRoot
.innerHTML = `<dialog><slot></slot><button>Close</button></dialog>`;
this.dialog = this.shadowRoot.querySelector("dialog");
}
connectedCallback() {
this.onclick = () => this.close(); // or attach to button
}
attributeChangedCallback(name,oldValue,newValue) {
this.open();
}
open() {
this.dialog.showModal(); // or .show()
}
close() {
this.dialog.close();
}
});
</script>
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'/>
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">
I am trying to make a VueJS plugin that exports a global method, which when called, will popup a message with an input text field. Ideally, I want to be able to make the following call from any Vue component:
this.$disaplayMessageWithInput("Title","Body","Value");
And a popup should come on the screen.
I've tried building it but when the install() calls this.$ref., it isn't recognized:
DeleteConfirmation.vue
<template>
<b-modal size="lg" ref="deleteConfirmationModal" :title="this.title" header-bg-variant="danger" #ok="confirmDelete" #cancel="confirmCancel">
<p>
{{this.body}}
</p>
</b-modal>
</template>
<script>
export default {
data()
{
return {
title: null,
body: null,
valueCheck: null,
value: null
};
},
install(vue, options)
{
Vue.prototype.$deleteConfirmation = function(title, body, expectedValue)
{
this.title = title;
this.body = body;
this.valueCheck = expectedValue;
this.$refs.$deleteConfirmation.show()
}
},
}
</script>
app.js
import DeleteConfirmation from './components/global/DeleteConfirmation/DeleteConfirmation';
Vue.use(DeleteConfirmation);
The call I am trying to make is:
$vm0.$deleteConfirmation("title","body","val");
I get the below error at the run time:
app.js?id=c27b2799e01554aae7e1:33 Uncaught TypeError: Cannot read property 'show' of undefined
at Vue.$deleteConfirmation (app.js?id=c27b2799e01554aae7e1:33)
at <anonymous>:1:6
Vue.$deleteConfirmation # app.js?id=c27b2799e01554aae7e1:33
(anonymous) # VM1481:1
It looks like, this.$refs in DeleteConfirmation.vue is undefined.
Try to avoiding $ref with vue ( $ref is here for third party and some very special case )
$ref isn't reactive and is populate after the render ...
the best solution for me is using a event bus like this :
const EventBus = new Vue({
name: 'EventBus',
});
Vue.set(Vue.prototype, '$bus', EventBus);
And then use the event bus for calling function of your modal ...
(
this.$bus.on('event-name', callback) / this.$bus.off('event-name');
this.$bus.$emit('event-name', payload);
)
You can create a little wrapper around the bootstrap modal like mine
( exept a use the sweet-modal)
<template>
<div>
<sweet-modal
:ref="modalUid"
:title="title"
:width="width"
:class="klass"
class="modal-form"
#open="onModalOpen"
#close="onModalClose"
>
<slot />
</sweet-modal>
</div>
</template>
<script>
export default {
name: 'TModal',
props: {
eventId: {
type: String,
default: null,
},
title: {
type: String,
default: null,
},
width: {
type: String,
default: null,
},
klass: {
type: String,
default: '',
},
},
computed: {
modalUid() {
return `${this._uid}_modal`; // eslint-disable-line no-underscore-dangle
},
modalRef() {
return this.$refs[this.modalUid];
},
},
mounted() {
if (this.eventId !== null) {
this.$bus.$on([this.eventName('open'), this.eventName('close')], this.catchModalArguments);
this.$bus.$on(this.eventName('open'), this.modalRef ? this.modalRef.open : this._.noop);
this.$bus.$on(this.eventName('close'), this.modalRef ? this.modalRef.close : this._.noop);
}
},
beforeDestroy() {
if (this.eventId !== null) {
this.$off([this.eventName('open'), this.eventName('close')]);
}
},
methods: {
onModalOpen() {
this.$bus.$emit(this.eventName('opened'), ...this.modalRef.args);
},
onModalClose() {
if (this.modalRef.is_open) {
this.$bus.$emit(this.eventName('closed'), ...this.modalRef.args);
}
},
eventName(action) {
return `t-event.t-modal.${this.eventId}.${action}`;
},
catchModalArguments(...args) {
if (this.modalRef) {
this.modalRef.args = args || [];
}
},
},
};
</script>
<style lang="scss" scoped>
/deep/ .sweet-modal {
.sweet-title > h2 {
line-height: 64px !important;
margin: 0 !important;
}
}
</style>
AppModal.vue
<template>
<div class="modal-wrapper" v-if="visible">
<h2>{{ title }}</h2>
<p>{{ text }}</p>
<div class="modal-buttons">
<button class="modal-button" #click="hide">Close</button>
<button class="modal-button" #click="confirm">Confirm</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
title: '',
text: ''
}
},
methods: {
hide() {
this.visible = false;
},
}
}
</script>
Modal.js (plugin)
import AppModal from 'AppModal.vue'
const Modal = {
install(Vue, options) {
this.EventBus = new Vue()
Vue.component('app-modal', AppModal)
Vue.prototype.$modal = {
show(params) {
Modal.EventBus.$emit('show', params)
}
}
}
}
export default Modal
main.js
import Modal from 'plugin.js'
// ...
Vue.use(Modal)
App.vue
<template>
<div id="app">
// ...
<app-modal/>
</div>
</template>
This looks pretty complicated. Why don't you use a ready-to-use popup component like this one? https://www.npmjs.com/package/#soldeplata/popper-vue