Categories
Vue Tips

Vue Tips — Making Components Play Nice with Each Other

Spread the love

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how Vue components can play nice with each other.

Assign Attributes to the Right Elements

We can pass attributes from a grandparent component to a grandchild component by setting the inheritAttrs option to false , and then add v-bind='$attrs' to the child of the grandparent to pass the attributes of the grandparent to the grandchild.

For instance, we can write the following code to do that:

index.js :

Vue.component("foo", {
  inheritAttrs: false,
  template: `
    <p v-bind='$attrs'>foo</p>
  `
});

new Vue({
  el: "#app"
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo class="foo"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

Then the foo class will be applied to the p element in foo since we set inheritAttrs to false in foo .

Use Browser Norms for Keyboard Navigation

We should follow the conventions for keyboard shortcuts for common operations if we’re going to handle keyboard shortcuts.

For instance, the tab key lets us move to different form fields. Enter is for submitting data, etc.

Use Events Over Callbacks

Emitting events from child to parent is better than callback functions that are passed in from parent to child.

For instance, we have to pass callbacks as props the following way:

index.js :

Vue.component("foo", {
  props: ["showAlert"],
  methods: {
    displayAlert() {
      if (typeof this.showAlert === "function") {
        this.showAlert("foo");
      }
    }
  },
  template: `
    <button @click='displayAlert'>Click Me</button>
  `
});

new Vue({
  el: "#app",
  methods: {
    showAlert(text) {
      alert(text);
    }
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo :show-alert="showAlert"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have the show-alert prop, which we pass the showAlert method from the root instance to the child. This is more complex and error-prone because we have to make sure that we passed in a function as the value of the prop.

We have to check that by using typeof of Vue’s built-in prop type validation.

Then we have to call the function that’s passed in as the value of the prop as we did in the displayAlert function. The name is different so it won’t clash with the showAlert prop.

On the other hand, if we emit events, then we won’t have to pass functions from parent to child to run something in the parent when something happens in the child.

For example, we can rewrite the example above by writing:

index.js :

Vue.component("foo", {
  template: `
    <button @click='$emit("button-clicked", "foo")'>Click Me</button>
  `
});

new Vue({
  el: "#app",
  methods: {
    showAlert(text) {
      alert(text);
    }
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <foo @button-clicked="showAlert($event)"></foo>
    </div>
    <script src="index.js"></script>
  </body>
</html>

The code above is a lot simpler. We just call $emit with the event name as the first argument and the payload that we want to send to the parent as the second.

Then we can listen to the button-clicked event from the parent as we did in index.html by writing:

@button-clicked="showAlert($event)"

Then $event object has the payload, which is 'foo’, so we can pass it straight into the showAlert method. Finally, we’ll see the alert display when we click the button.

Limit In-Component Styles

Scoped in-component styles in single-file components are only applied to the component itself. If we’re reusing it in many locations or apps, then we should let the styles be in that app instead so that we won’t have conflicts of styles between our component and the styles that’s already in the app.

This makes styling reusable components hard since we’ve to deal with conflicts.

Therefore, we should either leave the styles out of the single-file component or have the ability to toggle them on and off.

For instance, we can toggle the styles with an is-styled prop as follows:

HelloWorld.vue :

<template>
  <div>
    <p :class="{'is-styled': isStyled}">{{ msg }}</p>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
    isStyled: Boolean
  }
};
</script>

<style scoped>
.is-styled {
  color: #42b983;
}
</style>

App.vue :

<template>
  <div id="app">
    <button @click="isStyled = !isStyled">Toggle Style</button>
    <HelloWorld msg="Hello Vue" :is-styled="isStyled"/>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  components: {
    HelloWorld
  },
  data() {
    return {
      isStyled: true
    };
  }
};
</script>

In the code above, we have :class to bind the class dynamically according to the value of the isStyled prop that we passed in in HelloWorld.vue

Then in App.vue , we can set the isStyled value and pass it into the is-styled prop as the value so that we can have the ability to turn our HelloWorld ‘s component’s styles on and off as we wish.

Conclusion

We can use v-bind='$attrs' to inherit attributes from parent to child. If we’re going to add keyboard shortcuts handling to our app, we should following the conventions of other apps to make using our app convenient.

Also, we should emit events instead of calling callbacks passed from parent to child.

Finally, if we have shared components, we should either leave out the styles or have the ability to toggle them on and off.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *