Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use style tags inside vuejs template and update from data model

Tags:

I would like to dynamically update styles inside style tags.

However creating a container model for Vue, removes the style tags. I know style tags should belong in the head of the page, but this is just for the sake of ease of use.

So what I would like to have is a wrapper with an element and style tags inside:

<div class="setting">
  <style>
    .setting input {
      background: {{bgColor}};
    }
  </style>
  <input class="setting" type="text" v-model="bgColor">
</div>

The value from the input should update the value of the css style. Whenever done with simple div elements this works, but style tags seem to be a problem

The javascript set up is the following:

new Vue({
    el: '.setting',
    data: {
      bgColor: 'red'
    }
});

However when the style tags have a specific id, this could work, but I can't bind it to an input field.

<style id="setting">
  #blue {
    background: {{bg}}
  }
  #blue:hover {
    background: {{bgHover}}
  }
</style>

<div id="blue"></div>

and the js:

new Vue({
    el: '#setting',
    data: {
      bg: 'blue',
      bgHover: 'red'
    }
});

Can someone help me understand how I can achieve updating values between style tags. jsfiddle set up

Thanks.

like image 732
ForDev Avatar asked Mar 27 '18 15:03

ForDev


People also ask

How do I add style tag to Vue component?

In Vue. js, we can add an inline style to our element by binding the style attribute in the HTML tag. For reference, :style is shorthand for v-bind:style . Inline styling can be done in two ways: using object syntax or array syntax.

How do I change my Vue style?

Vue. js provides style binding that you can use to change the style dynamically. You can bind a variable to the style attribute in any HTML tag and change the style when the bound variable is changed.

How can you bind styles in VUE JS?

Binding Styles Dynamically To this end, Vue provides us with the v-bind:style directive. On each click event, the v-bind:style directive increments and decrements the value of your fontSize variable. This attaches the value of fontSize to the CSS font-size property.

How do I use props in Vue template?

To specify the type of prop you want to use in Vue, you will use an object instead of an array. You'll use the name of the property as the key of each property, and the type as the value. If the type of the data passed does not match the prop type, Vue sends an alert (in development mode) in the console with a warning.


2 Answers

vue-loader (and Vue template compiler Vue.compile(..)) both will filter out any <style> tags that are encountered in the template.

A simple solution to get around this, is to take advantage of Vue's built-in<component> 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>
like image 22
Troy Morehouse Avatar answered Sep 19 '22 13:09

Troy Morehouse


Here's what I think is a good workaround/solution.

It is just a custom component, so it's as reusable as it gets. All of Vue's goods like v-if can all be used.

Another pro is that the styles generated will be there only as long as the component is!

Vue.component('v-style', {
  render: function (createElement) {
    return createElement('style', this.$slots.default)
  }
});


// demo usage, check the template
new Vue({
  el: '#app',
  data: {
    bgColor: 'red'
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app" class="stuff">
  <v-style>
    .stuff input {
      background: {{bgColor}};
    }
  </v-style>

  Remove "red" and type "yellow":
  <input class="setting" type="text" v-model="bgColor">
</div>

The one drawback I see is that since the name of the tag is <v-style> (or whatever you chose to call it) and not <style>, the IDEs may not color it nicely. But otherwise it'll just be like a regular <style> tag.


Standard solution: using v-bind:style

This doesn't modify style tags, but the standard way of setting styles is using object style bindings.

Basically you'd use a :style attribute and assign to it the CSS properties of the style in the form of an object. Demo below.

new Vue({
  el: '.setting',
  data: {
    bgColor: 'red'
  },
  computed: {
    inputStyles() {
      return {
        background: this.bgColor
      }
    }
  }
});
<script src="https://unpkg.com/vue"></script>

<div class="setting">
  Remove "red" and type "yellow":
  <input class="setting" type="text" v-model="bgColor" :style="inputStyles">
</div>
like image 70
acdcjunior Avatar answered Sep 20 '22 13:09

acdcjunior