Vue 2 - render components from an ajax response - javascript

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!

Related

Putting an element inside Svelte Component

What does putting an element inside Svelte Component mean?
Eg. this code:
const target = document.createElement('div');
// render the component in the new element
const sample = new Sample({ target });
Like, here, in the given linked code, author is doing that:
https://github.com/rspieker/jest-transform-svelte/blob/master/example/test/Sample.spec.js#L8
What does this do? Is it putting Svelte component inside a div? Is it a Svelte syntax to put the element inside the constructor of the Svelte component?
Yes, that snippet is initializing the Svelte component named Sample and rendering it within the target div. The target property of a Svelte component constructor's options parameter is the only required property.
For more information, check out Svelte's components documentation.
It is the place where in your document the component will be rendered. Normally you would use a very specific location like body or a div with a certain id.
In this case however you are not actually rendering a page but merely testing a component so it doesn't matter where the div is.
You can find more info on testing with Jest here https://jestjs.io/

Vue components in user-defined markdown

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.

Vue.js - component inside component

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.

Accessing Parent Properties in Nested Components

In anticipation of Routable Components coming soon, I'm attempting to use Components wherever possible in my Ember 2.0 application. I'm running into a confusing issue where I cannot access the parent component's properties from the template when provided in block form. It may be very well that this isn't possible, but wanted to be sure. Here's an example:
Template:
// templates/approvals.hbs
{{#x-secure/schedule/approvals}}
{{#x-secure/layout/full sectionTitle=sectionTitle sectionDescription=sectionDescription}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Component Template:
// templates/components/schedule/approvals.hbs
{{yield}}
Component:
// components/schedule/approvals.js
import Ember from 'ember';
export default Ember.Component.extend({
/////////////////////////////////////
// PROPERTIES
/////////////////////////////////////
sectionTitle: 'Scheduling: Approvals',
sectionDescription: 'Lots of magical , fanstastic stuff.'
});
The issue I'm having is that I'm unable to access sectionTitle and sectionDescription from the parent component (approvals) and pass it into the layout/full component. However, if I remove code from the block of the component and move it to the templates/components/schedule/approvals.hbs template, it works as expected. Is it just not possible to access a parent component's properties from the block form of a component?
Thanks!
It is not possible indeed. The component's properties are available in the component's template, but not in the template that instantiates the component.
If you need the component to make things available, it should yield them explicitly:
// templates/components/schedule/approvals.hbs
{{yield sectionTitle sectionDescription}}
And using the component:
// templates/approvals.hbs
{{#x-secure/schedule/approvals as |title description|}}
{{#x-secure/layout/full sectionTitle=title sectionDescription=description}}
...
{{/x-secure/layout/full}}
{{/x-secure/schedule/approvals}}
Notice the as |x y ...| notation to assign names to yielded values.
Anything may be yielded this way, including this (be careful with that though, that breaks encapsulation) and actions.

Is my React component being recreated instead of updating?

I am trying to combine Angular and React.js. I have an work example project here I have seen a couple of ways to bring the Angular and React.js together. One of the methods I have seen is to create a directive and create the React component in the link function. For example in the first part of the project to generate the React version(in red) I am using
.directive('reactElementRepeater', function() {
return {
link: function(scope, element) {
var update_react = function(oldVal, newVal){ //Called every time one of the two values change
React.renderComponent(Demo_Element({
numberOfElements: scope.myModel.numberOfElem,
numberInElements: scope.myModel.numberInElem
}), element[0]);
}
scope.$watch('myModel.numberOfElem.length', update_react);
scope.$watch('myModel.numberInElem', update_react);
}
}
});
What I want and what should happen in a React enabled application is for something in the model to be updated, then that update is sent through React and it will alter the DOM as little as possible to reflect that change. It looks like that instead of updating a bit of the DOM this will Create a new React component each time with renderComponent.
React.renderComponent() instantiates the root component, starts the
framework, and injects the markup into a raw DOM element, provided as
the second argument.
Is it actually recreating the elements each time? If that is the case is there a way to alter this so that doesn't happen?
Just to be clear I know about ngReact, I just want to know other ways to speed up Angular with React.
Yes this is fine, it's not mounting the component multiple times.
When you call React.renderComponent() the second argument is the element which react should render the component to. So react notices if you are rendering the same component to a dom element that already contains a mounted instance of the component, and does not re-mount the entire component, it just updates the properties of it instead.
You can see this in action if you make a component with componentDidMount function defined. You'll notice that componentDidMount will only execute the first time renderComponent gets called. And afterwards, subsequent calls to renderComponent on the same target dom element will not call it because the component is already mounted. Likewise getDefaultState and getDefaultProps also only get called on the first renderComponent call.
If you're asking will the render function of the component be called every time the answer is yes. But this is how react works, you want the render function to get called because props might have changed. You can block it from being called by using shouldComponentUpdate (http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) and returning false. However react developers recommend you don't use this to block render calls unless you have specific performance problems - most of the time it should be fine to just let the render call execute as it wont cause any slow dom updates unless things have actually changed.

Categories