VueJs Directives: Class and Style Binding

Created on April 5, 2022.


For the most part up to this point, we have been dealing with just laying out the markup of the templates in our Vue applications. In this post, we are going to deviate a bit and focus on styling of our HTML markups.

Not only do we style our HTML documents to make them look better but also notify the users of changes in app states. Sometimes we spin loaders to show that something is being processed in the background or show specific colored notifications with accompanying messages to signal process completion or failure.

Binding Options

We can package component styles in named selectors, i.e. the id and class attributes then change the selectors being bound to our components on state changes. We can also in-line our styles within HTML elements with the style attribute and directly change the styles when the state changes.

The choice to go with one or the other depends on size and complexity of the project being worked on. In complex apps the earlier method is recommended as it makes the code readable and easy to maintain.

The binding of the selectors or the in-line style attribute is done with the v-bind directive.

Binding Using In-line Styles

Let’s start with a simple example.

Practice this code!
<html>
  <div id="app">
    <div :style="`background-color: ${color}; height: ${size}; width: ${size}; padding: 10px`">
      I am a box
    </div>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          size: 100,
          color: 'cyan'
        }
      }
    }).mount("#app");
  
</script>
</html>

As we can see in the above example, binding in-line styles with v-bind while using the shortcode :, just as how we would bind any other attribute using this directive.

Vue also allows us to bind both the style and class attributes in addition to them existing previously on the same element, this allows the separation of the unchanging styles such as padding: 10px above and the bound styles. This separation helps us maintain clean code.

Add the following div block to the above template and compare the two boxes.

Add code to previous example's template!
<div style="padding: 10px" :style="`background-color: ${color}; height: ${size}; width: ${size}`">
  I'm box 2
</div>

Binding Using the Class Attribute

Let’s see a style binding example using an element class attribute.

Practice this code!
<html>
  <style>
    .bulb{
      width: 100px;
      height: 100px;
      border: 3px solid gainsboro;
      border-radius: 50%;
    }
    .on{
      background-color: yellow;
    }
    .off{
      background-color: cornsilk;
    }
  
</style>

  <div id="app">
    <div class="bulb" :class="isOn ? 'on' : ''">
    </div>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          isOn: true
        }
      }
    }).mount("#app");
  
</script>
</html>

In the above example, we are changing the state of the light bulb between on and off depending on the state of the isOn variable.

When the isOn variable is truthy, the on class gets appended to the existing class="bulb" resulting to class="bulb on", consequently applying the .on class styles included in our style block above to our bulb, when otherwise it is replaced with an empty String removing those styles.

Try changing the value of isOn to see the resulting changes.

Binding Classes and Styles to Objects

We can opt to further simplify the binding of classes and in-line styles by using Objects in cases where we have an on and off state to our styling such as with the light bulb example above.

Let’s modify that example using this method.

Add this code to the template in the above example
<div id="app">
  <h2>Binding to Objects</h2>
  <div class="bulb" :class="{on: isOn}">
  </div>
  <br>
  <div class="bulb" :style="{backgroundColor: onColor}">
  </div>
</div>

 

Add this code to the javascript in the above example!
let app = Vue.createApp({
  data() {
    return {
      isOn: true,
      onColor: 'yellow'
    }
  }
}).mount("#app");

We can pass as many properties as possible using the Object method.

Practice this code!
<html>
  <style>
    .bulb{
      width: 100px;
      height: 100px;
      border: 3px solid gainsboro;
      background-color: cornsilk;
    }
    .round {
      border-radius: 50%;
    }
    .on{
      background-color: yellow;
    }
  
</style>

  <div id="app">
    <h2>Binding to Objects with multiple properties</h2>
    <div class="bulb" :class="{round: isRound, on: isOn}">
    </div>
    <br>
    <div :style="{width: size + 'px', height: size + 'px', backgroundColor: offColor, 'border-radius': 50 + '%', borderWidth: 3 + 'px', borderColor: 'gainsboro', borderStyle: 'solid'}">
    </div>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          isOn: true,
          isRound: false
        }
      }
    }).mount("#app");
  
</script>
</html>

While binding in-line styles using Objects, we name CSS properties inside the Objects using their exact Strings or by using the camel case convention. Refer to backgroundColor and 'border-radius' in the example above.

We can remove the in-line Object from our element’s bound class attribute and place it inside the data property, moving the logic from the template completely, only to bind the resulting Object to it. Given this possibility, we can also use computed properties in style binding.

Let’s demonstrate this in the following example.

Practice this code!
<html>
  <style>
    .bulb{
      width: 100px;
      height: 100px;
      border: 3px solid gainsboro;
      background-color: cornsilk;
    }
    .round {
      border-radius: 50%;
    }
    .on{
      background-color: yellow;
    }
  
</style>

  <div id="app">
    <h2>Binding to Objects with data properties</h2>
    <div class="bulb" :class="squareBulbOn">
    </div>
    <br>
    <div :style="roundBulbOff">
    </div>

    <h2>Binding to Objects from computed properties</h2>
    <div class="bulb" :class="fenceLights">
    </div>
    <br>
    <div :style="lawnLights">
    </div>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          size: 100,
          offColor: 'cornsilk',
          isOn: true,
          isRound: false,
          squareBulbOn: {
            on: true,
            round: false
          },
          roundBulbOff: {
            width: 100 + 'px',
            height: 100 + 'px',
            backgroundColor: 'cornsilk',
            borderRadius: 50 + '%',
            borderWidth: 3 + 'px',
            borderColor: 'gainsboro',
            borderStyle: 'solid'
          },
          dayLight: true
        }
      },
      computed: {
        fenceLights(){
          return { on: this.dayLight, round: false };
        },
        lawnLights(){
          return {
            width: `${this.size}px`,
            height: `${this.size}px`,
            backgroundColor: this.dayLight ? this.offColor : 'yellow',
            borderRadius: 50 + '%',
            borderWidth: 3 + 'px',
            borderColor: 'gainsboro',
            borderStyle: 'solid'
          };
        }
      }
    }).mount("#app");
  
</script>
</html>

As you can see, we have made our template code shorter, easy to read, and maintainable while still getting the same result.

Binding Classes and Styles to Arrays

In addition to binding using Objects, we can also use Arrays to bind element styles and classes.

Arrays give us beyond the Boolean state operations we get to use to define classes we get with Objects, especially when targeting to maintain a minimal code-base. We get to define several states within a single data property which is a more realistic CSS stance since most properties happen to have more than two states. We can also use Objects within Arrays, hence further minimizing the logic within our templates.

Let’s observe the following example.

Practice this code!
<html>
  <style>
    .bulb{
      width: 100px;
      height: 100px;
      border: 3px solid gainsboro;
      background-color: cornsilk;
    }
    .round {
      border-radius: 50%;
    }
    .on{
      background-color: yellow;
    }
  
</style>

  <div id="app">
    <h2>Binding to Arrays</h2>
    <div :class="[bulb, roundBulb, lightsOn]">
    </div>
    <br>
    <div :class="[bulb]" :style="[{backgroundColor: lightsOffColor}, {'border-radius': 50 + '%'}]">
    </div>

    <h2>Binding to Arrays with data properties</h2>
    <div class="bulb" :class="squareBulbOn">
    </div>
    <br>
    <div :style="roundBulbOff">
    </div>

    <h2>Binding to Arrays from computed properties</h2>
    <div :class="fenceLights">
    </div>
    <br>
    <div :style="lawnLights">
    </div>
  </div>

  <script src="https://unpkg.com/[email protected]"></script>
  <script>
    let app = Vue.createApp({
      data() {
        return {
          size: 100,
          bulb: 'bulb',
          roundBulb: 'round',
          lightsOn: 'on',
          lightsOffColor: 'cornsilk',
          lightsOnColor: 'yellow',
          squareBulbOn: [
            {on: true},
            {round: false}
          ],
          roundBulbOff: [
            {width: 100 + 'px'},
            {height: 100 + 'px'},
            {backgroundColor: 'cornsilk'},
            {borderRadius: 50 + '%'},
            {borderWidth: 3 + 'px'},
            {borderColor: 'gainsboro'},
            {borderStyle: 'solid'}
          ],
          dayLight: true,
          fancyLights: false
        }
      },
      computed: {
        fenceLights(){
          return [ this.bulb, {on: this.dayLight}, 'border-radius: 50%' ];
        },
        lawnLights(){
          return [
            `width: ${this.size}px`,
            `height: ${this.size}px`,
            'background-color: ' + (this.dayLight ? this.lightsOffColor : this.lightsOnColor),
            {borderRadius: 50 + '%'},
            {borderWidth: 3 + 'px'},
            {borderColor: 'gainsboro'},
            {borderStyle: 'solid'}
          ];
        }
      },
      mounted(){
        const dayAndNight = () => {
          if(this.fancyLights){
            this.dayLight = !this.dayLight
          } else {
            clearInterval(toggleId);
          }
        }
        let toggleId = setInterval(dayAndNight, 500)
      }
    }).mount("#app");
  
</script>
</html>

Looking closer at the example above, we realize that binding with Arrays, enables us to combine all of what we’ve learned in this post and put it together to get desired results.

Change the value of fancyLights in the example above to see some cool lights in action.

Next

VueJs Directives: Form Binding and Event Handling

Previous

VueJs Directives: List Rendering

Subscribe to the VueNoob Newsletter

[[[ noty.message ]]]

By using this site, you agree that you have read and understand its Privacy Policy.