Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js interprets `<` and `>` in string literal as custom element

Tags:

vue.js

I have a simple Vue.js app, with this template:

<h1>{{ name || '<New Document>' }}</h1>

My goal is that if name is falsy, to use the text <New Document>. This is not intended to be a custom markup tag. I want Vue.js to insert this into the document:

<h1>&lt;New Document&gt;</h1>

Instead, I get this warning on the console:

[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.

According to the documentation, using a pair of curly brackets, {{ and }}, means text interpolation, and the text value of that expression will be used. Instead, Vue.js seems to want to treat it as HTML.

Why is that happening? How can it be resolved?

like image 697
Brad Avatar asked Jan 06 '19 05:01

Brad


1 Answers

This is a great question. Like you, I assumed that everything between the curly braces would be evaluated as an expression and injected into the template. That's what the documentation implies, and in all cases I've encountered this appears to be true... except when there could be an HTML tag in a string literal. At least, that's why my experiments seem to indicate.

Take the following example:

<h1>{{ name || '<span>hello</span>' }}</h1>

There are two ways the parser could read this:

  1. An h1 tag containing curly braces - within those, an expression we need to evaluate later.
  2. An h1 tag followed by the string {{ name || ', then a span, then another string ' }}.

In this case, the parser is built to choose (2), and this explains why you received a compile-time error. The parser indicated that you have a tag starting with <New Document>, but it didn't have a corresponding closing tag.

If this seems like an odd design choice, consider this code:

<h1>{{'<span>hello</span>'}}</h1>

What did the user intend here? Did he/she mean to surround a span with curly braces and quotes? Probably.

As for a solution, you could manually escape the string:

{{ name || '&lt;New Document&gt;' }}

Alternatively, you could solve this with a computed property, which will eschew the template parser altogether:

<template>
  <h1>{{ nameOrNew }}</h1>
</template>

<script>
export default {
  data() {
    return {
      name: false,
    };
  },
  computed: {
    nameOrNew() {
      return this.name || '<New Document>';
    },
  },
};
</script>
like image 162
David Weldon Avatar answered Oct 10 '22 04:10

David Weldon