Pass native dom elements to vue application and render - vue.js

Is it possible to pass native DOM elements (rendered server-side) to a vue application?
Simple markup:
<div class="c-search"></div>
Will be "mounted" to a vue application by:
new Vue({
render: (h) =>
h(NameOfTheComponent),
}).$mount(document.querySelector('.c-search'););
So far, so good. But what if I want dom elements inside this html container to be passed to the application and render e.g. in a slot?
Markup:
<div class="c-search">
<div class="c-search__disclaimer">
<h1>This disclaimer text is rendered from a cms and should be rendered within the vue application</h1>
</div>
</div>
Currently, I pass the div as a prop and render it by v-html, but this is vulnerable and not recommended by the vue documentation. Is there a way to access child nodes of the application wrapper and re-render?

Related

how can i pass registered vue component as prop to a component

Need to pass component to child component, using props or you have better way to solve this problem
I registered component needed to pass globally or just registered this component locally. But these solutions can't solve my problem.
Here is my code to register component needed to pass locally:
a.vue html
<dropdown :icon="UserIcon"></dropdown>
a.vue js
components: {'dropdown', Dropdown, 'icon-user': UserIcon}
dropdown.vue html
<div class="dropdown"><icon></icon></div>
dropdown.vue js
props: ['icon']
UserIcon.vue
<i class="user-icon"></i>
the browser reminds me that icon is unknown custom element. It seems like vue does not support this way to use component, doesn't it?
I solve this problem using slot.
and there is another question comes...
a.vue
<dropdown><template v-slot:icon><icon-user></icon-user></template></dropdown>
dropdown.vue
<div class="dropdown"><slot name="icon"></slot></div>
But icon-user component didn't show...
and I did this work, cuz I remove the name attribute of slot.
a.vue
<dropdown><template><icon-user></icon-user></template></dropdown>
dropdown.vue
<div class="dropdown"><slot></slot></div>

vue.js Mount component to app root

I have a modal.vue component as follows:
<template>
<transition name="modal-transition">
<div class="modal-body" v-if="displayed">
<div class="modal-overlay" #click="displayed = false"></div>
<div class="modal-content">
<slot/>
</div>
</div>
</transition>
</template>
How do I mount this component to the applications root element rather than in place?
For crude inaccurate example:
<body>
<div id="app">
<div class="header"></div>
<div class="nav"></div>
<div class="stage">
<div class="sub-nav"></div>
<div class="content">
<modal :display.sync="display">MY MODAL</modal> <-- Don't mount here...
</div>
</div>
<-- Mount here instead...
</div>
</body>
The current issue is that my sites header and navigation is layered on top of my modal and it's darkened full screen overlay instead of layered behind the modal overlay.
Update for Vue 3
There is now a built in feature called teleport which allows mounting parts of your component template to any DOM element.
The example from the OP would look like something like this
<!-- MyModal.vue -->
<template>
<transition name="modal-transition">
<div class="modal-body" v-if="displayed">
<div class="modal-overlay" #click="displayed = false"></div>
<div class="modal-content">
<slot/>
</div>
</div>
</transition>
</template>
<!-- SomeDeeplyNestedComponent.vue -->
<template>
<teleport to="#app">
<!-- Can still receive props from parent -->
<MyModal :my-prop="foo">
<!-- slot content -->
</MyModal>
</teleport>
</template>
Vue 2
Move the elements own self to the element of applications root may be achieved in two ways, Using a portal as a preferred solution or using an append.
Using a Portal (Preferred Method)
PortalVue is a set of two components that allow you to render a
component's template (or a part of it) anywhere in the document - even
outside the part controlled by your Vue App!
https://portal-vue.linusb.org/
Using an Append (Not best practice)
If adding a portal library is too heavy, using an append is allowed but lightly discouraged officially in the VUE docs.
Typically this particular mount position will satisfy a z-index overlay for your own modal or dialog popup that you require to render over the top of the entire app. You can always substitute this.$root.$el in this example for a different element target using standard getElementBy or querySelector functions.
Here the element is being moved not destroyed and re-added, all reactive functionality will remain in tact.
<script>
export default {
name: 'modal',
...
mounted: function() {
this.$root.$el.append(this.$el);
},
destroyed: function() {
this.$el.parentNode.removeChild(this.$el);
}
}
</script>
On mounted the element is moved inside of where the top level VUE app instance is mounted.
On destroyed removes the placeholder DOM comment for the migrated component from the new parent to prevent orphaned duplication each time the component remounts it's self.
VUE officially states not to destroy an element outside of VUE so this is not to be confused with that statement, here the component has already been destroyed.
This DOM comment duplication will typically happen when for example switching views with vue-router as this mechanism mounts and dismounts all components in a router view each time vue-router view state changes.
This behaviour is a bug cause by vue-router, the object is destroyed properly by VUE render manager but an index reference remains by mistake, using a portal package resolves this issue.
Here is the result:

Correct way to use Vue component mutiple times in one page

I am trying to create a simple 24 hour time input that I can use across multiple browsers.
In the past I have used Vue to create components which are used once on a page, and I attach them to an id as follows:
<script>
$(function () {
new Vue({
el: "#my-time-element"
});
});
</script>
But what happens when I want to use it multiple times in one page? I tried attaching it using a class definition (attach to .my-element) but it only renders for the first matching element. (The component is registered globally.)
Perhaps I am supposed to wrap the page in a single Vue component, then embed my time component in its template? Perhaps I need to make a jQuery collection of all .my-element elements, then cycle through and instantiate Vue for each one? What is the correct way to approach this?
Perhaps I am supposed to wrap the page in a single Vue component, then embed my time component in its template?
That is definitely the easiest way to do it.
You do not have to change so many things compared to your regular HTML page: simply make sure to provide an ID to one of your root container, then use your Custom Component tag anywhere within and as many times you like.
You just need to prepare Vue by globally declaring your custom component first, with Vue.component:
Vue.component('my-component', {
template: '#my-component',
});
new Vue({
el: '#app',
});
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
<h1>Hello World</h1>
<my-component>Component instance 1</my-component>
<my-component>Component instance 2</my-component>
</div>
<template id="my-component">
<div style="border: 1px solid black">
<h2>My component</h2>
<slot></slot>
</div>
</template>
Perhaps I need to make a jQuery collection of all .my-element elements, then cycle through and instantiate Vue for each one?
That is also a possible solution, but probably defeats the Vue spirit…

Aurelia custom element without outer node

Is it possible to let Aurelia render a custom element without the capsulating component node? Or replace the custom-element node with its content?
Example:
app.html
<template>
<require from = "./components/custom-component.html"></require>
<custom-component></custom-component>
</template>
app.ts
export class App {
}
custom-component.html
<template>
<p>This is some text from dynamic component...</p>
</template>
Result
Based on this example: Is it possible with aurelia to render the <p> element from the component as direct child of the <body>, so that there will be no custom-component-node?
Use the containerless attribute on your component.
example: https://gist.run/?id=8e57000c7b8423dc0246a7006d90ba79
you can also decorate your custom components with the containerless() decorator.
see: http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/cheat-sheet/9

Accessing properties/methods of a Vuejs component globally

I want to create a custom component and to use it like
<div id="app">
<router-view />
<my-custom-loader />
</div>
My component has some methods which I want to use globally anywhere like
this.$loader.show();
this.$loader.hide();
I can create the component but not sure how to inject its method/properties so that it's available everywhere.
Component create a encapsulated scope in vuejs.
AFAIK you should not be doing this as I feel this to be more like a workaround that a perfect vuejs way of doing this.
You can setup a ref on the <my-custom-loader> component.
<div id="app">
<router-view />
<my-custom-loader ref="loader"/>
</div>
Since the component looks like the child of root vue instance mounted on #app you can use it to acess $refs
var vm = new Vue({
el: '#app'
});
//my-custom-loader component's instance
var loader = vm.$rers.loader;
Then you can setup this loader which is the my-custom-loader component's instance on the Vue.prototype. This now allows you to acess $loader in any component using this.$loader
Vue.prototype.$loader = loader;
Now you can acess its methods using
this.$loader.hide();
this.$loader.show();
keep in mind the warning of using refs as mentioned in the docs
$refs are only populated after the component has been rendered, and it is not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid using $refs in templates or computed properties.
As for me I recommend you use custom events or register a global event bus or use vuex if your app is very big and has complex state management