VueJs templating. How to load external templates
JavascriptJqueryAngularjsvue.jsJavascript Problem Overview
I'm new to Vue.js, I've used AngularJS for some time and in angular we used to load templates such as,
template: '/sometemplate.html',
controller: 'someCtrl'
How can we do such a thing in Vue, instead of keeping large HTML templates inside JavaScript like this,
new Vue({
el: '#replace',
template: '<p>replaced</p>'
})
This is OK for small templates but for large templates is this practical?
Is there a way to load external template HTML or use HTML template inside a script tag like in Vue?
<script type="x-template" id="template">HTML template goes here</html>
Javascript Solutions
Solution 1 - Javascript
You can use the script tag template by just referring to its id
.
{
template: '#some-id'
}
Though, I highly recommend using vueify (if you use browserify) or vue-loader (if you use webpack) so you can have your components stored in nice little .vue
files like this.
Also, the author of Vue wrote a nice post about the topic of external template urls:
Solution 2 - Javascript
You can try this:
for Vue2 : https://github.com/FranckFreiburger/http-vue-loader
for Vue3 : https://github.com/FranckFreiburger/vue3-sfc-loader
Example (Vue2) :
new Vue({
components: {
'my-component': httpVueLoader('my-component.vue')
},
...
Example (Vue3) :
Vue.createApp({
components: {
'my-component': Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', opts))
},
...
Solution 3 - Javascript
David, that is a nice example, but what's the best way to make sure the DOM is compiled?
https://jsfiddle.net/q7xcbuxd/35/
When I simulate an async operation, like in the example above, it works. But as soon as I load an external page "on the fly", Vue complains because the DOM is not ready.
More specifically:
Uncaught TypeError: Cannot set property 'vue' of undefined
Is there a better way to do this than to call $compile
when the page has loaded? I've tried with $mount
, but that didn't help.
UPDATE: Never mind, I finally figured out how to do it:
Vue.component('async-component', function (resolve, reject) {
vue.$http.get('async-component.html', function(data, status, request){
var parser = new DOMParser();
var doc = parser.parseFromString(data, "text/html");
resolve({
template: doc
});
});
});
And in the actual template, I removed the
<script id="someTemplate" type="text/x-template"></script>
tags and only included the html.
(This solution requires the http loader from https://cdnjs.cloudflare.com/ajax/libs/vue-resource/0.1.10/vue-resource.min.js)
Solution 4 - Javascript
I've tried http-vue-loader and it works fine. This library is easy to use and has good documentation and examples
Although you can't load templates from filed directly you still can keep html in separate single-file components. You can even skip <script>...</script>
part.
Usage (from loader's documentation)
my-component.vue
<template>
<div class="hello">Hello {{who}}</div>
</template>
index.html
<!doctype html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
</head>
<body>
<div id="my-app">
<my-component></my-component>
</div>
<script type="text/javascript">
new Vue({
el: '#my-app',
components: {
'my-component': httpVueLoader('my-component.vue')
}
});
</script>
</body>
</html>
Both files should be placed in one folder at the same level
Solution 5 - Javascript
1. In Vue 2.x I recommend sticking with convention by using .vue files but instead, inverting the order of imports:
// template.vue
<template>
<div class="helloworld">
<h1>Hello world</h1>
</div>
</template>
<script>
import src from './src'
export default src
</script>
and in a separate file
// src.js
export default {
name: 'helloworld',
props: {},
...
}
Then in your component registration
import helloworld from './helloworld/template.vue'
new Vue({
components: {
'helloworld': helloworld
},
...})
This way you get the best of both worlds and you don't have to force yourself to build templates within a string.
2. If you want to lazy load, apparently there is a way to do so in Vue 2.x
new Vue({
components: {
'helloworld': () => import(/* webpackChunkName: "helloworld" */ './helloworld/template.vue')
},
...})
This will load helloworld.js (which will contain all that component's code) on request of that page in the browser.
Of course, all the above assumes you are using ES6 with import
capabilities
Solution 6 - Javascript
there are at least 2 ways to achieve what you want, on of them (x-templates) already mentioned by Bill Criswell, but I think it worth to add an example
-
Define your component like this
Vue.component('my-checkbox', { // id of x-template template: '#my-template' });
-
Add html file with your x-template (id should match the one you specified in the component)
<script type="text/x-template" id="my-template">...</script>
Another approach (and I like this one better) would be to use inline template
-
Define your template like this
Vue.component('my-template', {});
-
Add html file with your component and template inside it
<my-template inline-template>place for your html</my-template>
Just don't forget to add inline-template
attribute, otherwise it won't work
Solution 7 - Javascript
You can use this approach with superagent:
var promise = superagent.get("something.html")
.end(function (error, response) {
if (error) {
console.error("load of something.html failed", error));
return;
}
var parser = new DOMParser()
var doc = parser.parseFromString(response.text, "text/html");
document.body.appendChild(doc.scripts[0]);
});
Just put your <script>
tag based template inside of something.html
on your server.
If you are using jQuery, .load should work.
Just make sure this completes before the DOM in question is compiled by Vue. Or use $mount to manually set things up.
Solution 8 - Javascript
Use browserify to bundle everything like this:
//Home.js
import Vue from 'vue';
var Home = Vue.extend({
template: require('./Home.vue')
});
export default Home;
//Home.vue
<h1>Hello</h1>
// And for your browserify bundle use a transform called stringify
... .transform(stringify(['.html', '.svg', '.vue', '.template', '.tmpl']));