About using style tags inside templates

Hi to all,

I’ve been tasked with creating a new single page application on a platform that provides generates content dynamically from a CMS in which style tags are frequently present. I can’t get rid of the tags or know their content in advance.

When I try to instantiate components, Vue complains about those tags:

Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as , as they will not be parsed.

Would patching vue.js’s isForbiddenTag function to allow style tags cause any issues I am not aware of?

function isForbiddenTag (el) {
    return (
        el.tag === 'style' ||
        (el.tag === 'script' && (
            !el.attrsMap.type ||
            el.attrsMap.type === 'text/javascript'
        ))
    )
}

btw, this question was similar to mine but doesn’t answer my question: Is the warning about script tags inside of a Vue component template only a “warning”?

Thanks in advance.

1 Like

Hello,

I hope this answer is not too late for you. I figured out a way to use style tags with Vue, but it’s kind of hacky and maybe not 100% what you are looking for. Never the less, let me share:

I want to dynamically set the content of a style tag, based on a computed property. As the styletag can’t live inside Vue and actually should also live in the head of the document, I create a node manually in the mounted function:

mounted () {

  let style = document.createElement('style')
  style.type = "text/css"
  style.appendChild(document.createTextNode(''))
  this.styleNode = style.childNodes[0] // a reference I store in the data hash
  document.head.appendChild(style)
}

Then I ended up using a computed property, or anything that returns a string I can use for styling.
A watch method for example reacts to my computed property and sets the content like so:

watch: {
  myStyleProp (val) {
    return this.styleNode.textContent = val
  }
}

Hope that helps a little. It kind of is against what Vue would want, to be solely data driven, but actually the data comes from inside Vue so I feel kind of good about it :slight_smile:

Best,
Roman

3 Likes

vue-loader (and Vue template compiler Vue.compile(..)) both will filter out any <style> tags that is encounters.

A simple solution to get around this, is to take advantage of Vue’s <component> built-in component.

<template>
  <div>
    <component is="style">
      .foo[data-id="{{ uniqueId }}"] {
        color: {{ color }};
      }
      .foo[data-id="{{ uniqueId }}"] .bar {
        text-align: {{ align }}
      }
    </component>
    <div class="foo" :id="id" :data-id="uniqueId">
      <div class="bar">
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    id: {
      type: String,
      default: null
    }
  },
  computed: {
    uniqueId() {
      // Note: this._uid is not considered SSR safe though, so you
      // may want to use some other ID/UUID generator that will
      // generate the same ID server side and client side. Or just
      // ensure you always provide a unique ID to the `id` prop
      return this.id || this._uid;
    },
    color() {
      return someCondition ? 'red' : '#000';
    },
    align() {
      return someCondition ? 'left' : 'right';
    }
  }
}
</script>

A unique ID (or some other data-attribute) is required to “scope” the styles to just this component.

This is a nice solution as you can use v-for loops to generate the style content if required (which can be reactive to changes in your components data/props/computed props)

<component is="style" type="text/css">
  <template v-for="item in items">
    [data-id="{{ uniqueId }}"] div.bar[data-div-id="item.id"]::before {
      content: "{{ item.content }}";
      color: {{ item.color }};
    }
  </template>
</component>
3 Likes

Hey man just wanted to say, thank you for posting this. This was really helpful for solving a particular problem I had in a app.

1 Like

Thank you so much for this. Got your post several days after struggling and almost giving up. Works perfect.