45
<template id="task-template">
    <h1>My Tasks</h1>
    <tasks-app></tasks-app>
    <ul class="list-group">
        <li class="list-group-item" v-for="task in list">
            {{task.body|e}}
        </li>
    </ul>
</template>

This above is my html. I want to render the code by Vue instead.

<script>
    Vue.component('tasks-app', {
        template: '#tasks-template',
        data: function() {
            return {
                list: []
            }
        }
        created: function() {
            $.getJson('/api/tasks', function(data) {
                this.list = data;
            })
        }
    })
    new Vue({
        el: 'body',
    });
</script>

The above is my Vue code, and Jinja raise an exception that 'task' is undefined, what I hope for is that the html code rendered by Vue instead of Jinja, I know it could be done in Laravel with this:

"@{{task.body}}"

Since I am new to Jinja, could anyone help me out?

T-Heron
  • 5,385
  • 7
  • 26
  • 52
ramsay
  • 3,197
  • 2
  • 14
  • 13

9 Answers9

89

The other option is to redefine the delimiters used by Vue.js. This is handy if you have a lot of existing template code and you wish to start adding Vue.js functionality to a Flask or Django project.

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  },
  delimiters: ['[[',']]']
})

Then in your HTML you can mix your Jinja and Vue.js template tags:

    <div id="app">
      {{normaltemplatetag}}
      [[ message ]] 
    </div>

Not sure when the "delimiters" property was added, but it is in version 2.0.

tony19
  • 125,647
  • 18
  • 229
  • 307
eezis
  • 2,103
  • 2
  • 19
  • 14
  • 9
    for everybody who is trying to implement vue.js as a front-end on a django-rest backend, this comment is a godsend. it saved me so much frustration and magically misrendered or un-rendered elements. – mburke05 Jul 25 '17 at 19:48
  • @mburke05 It's also possible to redefine the delimiters used by jinja2 - it all depends on which one you document as what. – Maximilian Burszley Dec 18 '19 at 15:21
  • This works in some cases, but doesn't seem to work within – Chris Austin Jun 02 '21 at 17:57
44

You need to define parts of your template as raw so that Jinja escapes that portion instead of trying to fill it up with its own context.

Here is how you need to do it:

<template id="task-template">
  <h1>My Tasks</h1>
  <tasks-app></tasks-app>
  <ul class="list-group">
    <li class="list-group-item" v-for="task in list">
        {% raw %}{{task.body|e}}{% endraw %}
    </li>
  </ul>
</template>

Ref: http://jinja.pocoo.org/docs/dev/templates/#escaping

Mani
  • 23,635
  • 6
  • 67
  • 54
16

If you're using Flask, you can redefine the delimiters used by Jinja:

class CustomFlask(Flask):
    jinja_options = Flask.jinja_options.copy()
    jinja_options.update(dict(
        variable_start_string='%%',  # Default is '{{', I'm changing this because Vue.js uses '{{' / '}}'
        variable_end_string='%%',
    ))


app = CustomFlask(__name__)  # This replaces your existing "app = Flask(__name__)"
Nathan Wailes
  • 9,872
  • 7
  • 57
  • 95
14

You can either change default VueJS or Jinja delimiters. I actually prefer to change VueJS delimiters like below:

new Vue({
  el: '#app',
  delimiters: ['${', '}']
})

Then you can use ${variable} instead of conflicting {{ var }}, see docs.

This matches with ES6 template string style, so it is preferable I would say. Keep in mind that you will have to do same when you crate new components.

Alternatively, you could just bypass Jinja template rendering altogether. Just move your index.html from templates directory to static directory (next to css, js files), then:

@app.route("/")
def start():
    return app.send_static_file("index.html")

If you are doing all view logic in VueJS, this can work.

tony19
  • 125,647
  • 18
  • 229
  • 307
chhantyal
  • 11,874
  • 7
  • 51
  • 77
5

Use {{ '{{ vue }}' }}

I had the same problem, and also got it solved.

Aniket G
  • 3,471
  • 1
  • 13
  • 39
4

Configure Vue v2 class instance with the 'delimiters' option:

<div id='myapp'> !{ message } </div>
<script>
let myapp = new Vue({ delimiters: ['!{', '}'], ...});
</script>

Source: https://v2.vuejs.org/v2/api/#delimiters

tony19
  • 125,647
  • 18
  • 229
  • 307
2

You can use directives

<h3 v-text="message"></h3>

Or you can use delimiters

const app = new Vue({
    el: '#app',
    delimiters: ['${', '}'],
    data: {
    message: 'a vue message'
    },
});

In your html page

<h3>${ message }</h3>
Murad
  • 1,064
  • 16
  • 13
1

you can use {% raw %} {% endraw %} in your html code

Huhu
  • 11
  • 1
1

I know this is old post, and however @Nathan Wailes answer helped me. I further tried avoiding moustaches {{myVal}} and used v-text="myVar" for rendering text. And I did not need to override jinja delimiters. see example below:

<!--Vuetify-->    
<v-btn class="ma-2" outlined color="indigo" v-text="connectToggleLabel"></v-btn>
<v-btn class="ma-2" outlined color="indigo"><span v-text="connectToggleLabel"/></v-btn>
<!-- basic -->
<input type="button" :value="connectToggleLabel"/>

Hope this help somebody

Mahesh
  • 982
  • 8
  • 20