1

I hope this is a trivial problem for someone and I am just missing a tiny magic piece of code: I am migrating from knockout.js to vue.js and I am wondering if there is a simple way to use templates the same way in vue.js as in knockout.js. The following code snipped uses knockout.js and works as expected:

function viewmodel() {
    this.person = ko.observable(new person());
}
function person() {
    this.first = ko.observable('hello');
    this.last = ko.observable('world');
}
ko.applyBindings(new viewmodel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script type="text/html" id="person-template">
    <p data-bind="text: first"/>
    <p data-bind="text: last"/>
</script>
<div>
    <div data-bind="template: { name: 'person-template', data: person }"/>
</div>

However, I can't figure out how to achieve the same in vue.js. I am aware of components in vue.js and all that, but I want to keep the structure as it is and not put more view-specific code into my JavaScript files. (I am already not happy with the el: '#app', but that's my least concern at the moment) Of course this code doesn't work:

var app = new Vue({
    el: '#app',
    data: {
        person: {
            first: 'hello',
            last: 'world'
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>

<script type="text/x-template" id="person-template">
    <p v-text="first"/>
    <p v-text="last"/>
</script>
<div id="app">
    <div><!-- how? is this even possible? --></div>
</div>

Is there a way to get this working by only changing the HTML part of code? Preferably only the <div>-Element.

Num Lock
  • 690
  • 4
  • 22

1 Answers1

4

So this is not quite only changing the template, but a single additional property can work here. Specify a template in the Vue.

var app = new Vue({
    el: '#app',
    template: "#person-template",
    data: {
        person: {
            first: 'hello',
            last: 'world'
        }
    }
});

I also modified your template slightly because a template for any Vue or component requires one root element. I recommend you do not use self closing tags in Vue as I've seen this cause issues. Vue requires valid HTML5 and self closing tags are not valid HTML5.

<script type="text/x-template" id="person-template">
  <div>
    <p v-text="person.first"></p>
    <p v-text="person.last"></p>
  </div>
</script>

Working example.

Additionally, a very common way of specifying templates in this fashion, without using the kind of hacky script tag is to use the HTML5 template element.

<template id="person-template">
  <div>
    <p v-text="person.first"></p>
    <p v-text="person.last"></p>
  </div>
</template>

Finally, knowing you are slightly unhappy with the el:"#app" You can leave the el property out of the Vue definition and mount the Vue on an element of your choice using the $mount method. So if you wanted to go completely hog wild on separation of concerns you could do something like this.

const renderer = Vue.compile(document.querySelector("#person-template").innerHTML)
const example = {
    data: {
        person: {
            first: 'hello',
            last: 'world'
        }
    }
}
const app = new Vue(Object.assign({}, example, renderer))
app.$mount("#app")

Example.

I should mention nobody really does that (compiling their templates manually, it's common to use $mount) that I'm aware of. It also depends on using the version of Vue that includes the compiler (which you might not have if you are using webpack or some other build tool for example). You could also do something like this:

const app = new Vue(Object.assign({}, example, {template: "#person-template"}))
app.$mount("#app")

which would basically allow you to specify a template, a component, and a place to mount it all separately.

Bert
  • 70,407
  • 13
  • 164
  • 146
  • @NumLock so, I had a few more ideas and added them after you accepted the answer. Just FYI. – Bert May 31 '17 at 15:24
  • Thanks. I _really_ appreciate all the effort. I am currently trying to grasp the implications about directly referencing `person` inside the template instead of just its properties. With my KO example I could just reuse the template for basically everything that has those two properties. As it is now (from my current understanding at least) I just offloaded how to render person instead of defining a (reusable) template – Num Lock May 31 '17 at 15:31
  • @NumLock You can always define a person component that just renders the person. – Bert May 31 '17 at 15:31
  • There is a user here, @RoyJ, that answers quite a few Vue and Knockout questions. You also might want to ping him with any questions transitioning from Knockout to Vue. – Bert May 31 '17 at 15:44