I need to instantiate zero or more Vue components within HTML generated by a markdown rendering plugin. The component count and props for each are defined in the markdown. (I control the markdown plugin.)
instead of normal template syntax:
<my-component :key="uid_var"></my-component>
markdown plugin would generate HTML:
<div id="uid">replaced by component</div>
which we pass to vue in a template:
<div v-html="md.render(src, {map:myCompMap})"></div>
This article suggests the following
https://css-tricks.com/creating-vue-js-component-instances-programmatically/
var ComponentClass = Vue.extend(MyComponent);
var instance = new ComponentClass({
propsData: { input: 'xyz' },
});
instance.$mount('#uid');
But this is user-written markdown, re-rendered on every keystroke, so we should re-use component instances. Questions...
Do I call instance.$mount('#uid') after each md.render() pass?
Do I call instance.$destroy() if a later md.render() pass drops a component?
Do I need a key attribute in the markdown, e.g. <div id="uid" key="uid">? I suspect the above achieves what a key provides in vue templates.
Related
I would like to be able to use the components already defined and used in my Gatsby site, for elements rendered from markdown nodes. Using styled-components I have multiple components representing html elements:
const PrimaryHeader = styled.h1`…`
const SecondaryHeader = styled.h2`…`
const TextList = styled.ul`…`
etc
Is there a mechanism for mapping generic html elements defined in the markdown across to react components, so that a value defined in the markup as:
# Example
Is rendered by my PrimaryHeader component for example.
It seems the only option for styling markup is using descendent selectors to target elements within the rendered markdown content which feels clumsy and unnecessary when I already have components defined for rendering these elements.
Note that Gatsby does support the mapping of custom components, but this involves custom html added to the markdown. I want the markdown to be generic.
Yes it does, and i've added a section to the Gatsby docs explaining how which is reproduced here:
Mapping from generic HTML elements
You can also map a generic HTML element to one of your own components. This can be particularly useful if you are using something like styled-components as it allows you to share these primitives between markdown content and the rest of your site. It also means the author of the Markdown doesn’t need to use any custom markup - just standard markdown.
For example if you have a series of header components:
const PrimaryTitle = styled.h1`…`
const SecondaryTitle styled.h2`…`
const TertiaryTitle styled.h3`…`
You can map headers defined in markdown to these components:
const renderAst = new rehypeReact({
createElement: React.createElement,
components: {
"h1": PrimaryTitle,
"h2": SecondaryTitle,
"h3": TertiaryTitle,
},
}).Compiler;
And headers defined in markdown will be rendered as your components instead of generic HTML elements:
# This will be rendered as a PrimaryTitle component
## This will be rendered as a SecondaryTitle component
### This will be rendered as a TertiaryTitle component
I have a scenario where I need to pass a list of different "components" to another angular component. I'm passing the list of components as an array using the #Input. How can I render those components in the HTML?
It would be helpful to see some code to have an idea about what you're trying to accomplish.
From the documentation, #Input()
An Input property is a settable property annotated with an #Input decorator. Values flow into the property when it is data bound with a property binding
An #Input() annotation is used for passing data. It's not used for passing components to another component.
To display another component inside another component, reference the nested component's selector inside the parent component. The selector is part of component metadata
selector: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains , then Angular inserts an instance of the HeroListComponent view between those tags.
app-hero-list.component.ts
Create a component and give a selector in the metadata.
#Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
app.component.html
Then you can reference that selector as a pair of HTML tags in another component's template.
<app-hero-list></app-hero-list>
Have a look at the component metadata link referenced above and the Angular Tutorial for some more information about displaying components in other components.
If you need a more detailed answer, please update your question with a code example.
Is there a way to render components that come from an ajax response?
For example, I registered a component "Test" and in the ajax response I have:
<p>dummy paragraph</p>
<test></test> <!-- vue component I want to render -->
<p>another dummy paragraph</p>
My final goal is to make a shortcode that lets the user insert a router link in the content area.
I'm using Vue 2, vue router
Edit: here is a demo https://jsfiddle.net/Paulius_Krutkis/4mb1ypqs/
I am not sure, what is the exact issue you are facing here, If you are looking for how to render the HTML returned from ajax call, You can use v-html which can render the string variable having HTML.
However be aware:
Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates.
So v-html will not compile and and render any vue component as you may expect, you may have to find some other solution for it.
Alternet way
However as you are saying, you need a way to render components that come from an ajax response, You can take help of Async-Components, where you define your component as a factory function that asynchronously resolves your component definition.
see the demo here: https://jsfiddle.net/4mb1ypqs/1/
Yes, there is way for doing this. Follow these steps:
Register the component (which will be sent as an ajax response) globally.
import Vue from 'vue'
import Test from './some/folder/Test.vue'
Vue.component('test', Test)
Either add Vue to the window object or export it and import it wherever you need it. Here i will add it to the global window object so that i can easily access it within the browser console.
window.Vue = Vue
When you get the ajax response with the uncompiled DOM element (<test></test>) you can decide if you want to insert it directly to your DOM and compile it afterwards or compile it first and then mount it to the DOM.
Lets say you inserted the component template directly to the DOM like in your example.
Notice the wrapper element.
<p>dummy paragraph</p>
<div id="testapp">
<test></test>
</div>
<p>another dummy paragraph</p>
Create a new Vue instance and tell it where it should mount. Since your test component is already registered globally, vue knows how it looks like and will compile it once it sees it in the DOM. Alternatively, you can also add it here within your "components" property.
new window.Vue({
el: '#testapp',
})
Done!
Info: VueDevtools will not recognize your new compiled component If you have already a root vue instance mounted initially. To make it work you must tell the newly created instance that it belongs to a parent instance:
var vm = new Vue({
el: '#app',
});
new window.Vue({
el: '#testapp',
parent: vm,
});
Notice: The new vue instance will NOT share data with other instances. This approach is only "meaningful" (of course this whole procedure is not recommended) when you have independent components like for example a text-input component, that is only there to send data to the server independently.
Hope it helps someone out there!
I do have my component called Grid. Inside this component I load JSON data from server and i render them. They are mostly string and integers. Sometimes the JSON contains HTML like <strong>myvalue</stong> so I render the data with three brackets {{{ and }}}.
The thing is when the HTML is not pure HTML but component like <my-component my-param="1"></my-component>. How to tell to Vue.js to render this coponent? All I get is the HTML purely printed into grid.
Thanks
You need to compile again that piece of code you've loaded from remote.
ps: I will use jQuery to manipulate the DOM.
Inside this component I load JSON data from server and i render them.
I'll assume you have a function named "loadAndRenderFromServer()", please adapt the code below to fits you.
Eg: If your grid has the markup <div id='grid'>
// vuecomponent.js
export default {
[...]
methods: {
loadAndRenderFromServer() {
// first, load remote content and insert into #grid
// now, compile
this.$compile($("#grid").get(0));
}
},
[...]
}
You may need to use "decompile" if your component starts to duplicate the loaded content. Check into VueJS docs for compile and decompile methods.
Using v-html (which is equivalent to {{{}}}) will not render what's inside it if it's a component.
Instead try to use <slot> in your parent template.
Otherwise, if you want dynamic components you need to use <component> and if you want content inside those dynamic component you need to use <slot>s.
I would suggest you to use something like
<component :is="myComponent" />
and inside the models of those components put some <slot>s to insert arbitrary content.
I am new to Ember and noticed the following code in my Handlebars:
{{component sec.myCompRef secInfo=sec fields=model.myMap}}
Does Ember have some helper like {{component}}? If yes, how does it work?
I have generally seen custom helper:
{{my-helper}}
But that has the same name component hbs/js backing it?
But I am not aware of {{component}} helper.
Yes, Ember has {{component}} helper which allows you to dynamically choose and render component via component name passed to {{component}} helper.
So, basically the usage is:
{{component componentName}}
You can of course pass arguments and data to it like you would do with casual component declaration:
{{component componentName model=whatever}}
So, in your case:
{{component sec.myCompRef secInfo=sec fields=model.myMap}}
sec.myCompRef is the name of component to render (it has to match some existing component)
secInfo and fields are parameters passed to component