Skip to content

Project Setup

Enter the following command from your Windows Command Prompt/PowerShell or Linux/OSX Terminal to install Vue CLI globally:

npm install -g @vue/cli

vue --version     # Verify installation (v 4.5.11)

If your system does not allow you to installed the CLI globally,

Data Binding

VueJS provides three mechanisms for binding data to your page, only the first two are described below. The third one will be explained later in the context of event handling:

  • Use the "mustache" operator {{xx}} to bind data as a text node in your HTML
  • Use v-bind to bind data to an attribute of HTML elements

Using The "Mustache" Operator

vue
<template>
  <div>
    <h2>{{message}}</h2>
    <!-- You can use any valid JS expressions -->
    <p>{{message.toUpperCase()}}</p>
    <span>Length of string:{{message.length + 2}}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello World"
    };
  }
};
</script>

Using v-bind

In the following example, the value of the width attribute of the <img> element is bound to the variable imgWidth. Alternatively, v-bind:width can be shortened to :width.

vue
<template>
  <div>
    <img
      src="https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
      v-bind:width="imgWidth" />
    <img
      src="https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
      :width="imgWidth" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      imgWidth: 150
    };
  }
};
</script>

Using v-bind with Dynamic Arguments

WARNING

This feature requires VueJS version 2.6.0 or newer

Practical use case, what if you need to include an image but, let's say, in landscape mode you want to bind its width and in portrait mode you want to bind its height. One solution is to use v-if and v-else:

html
<img v-if="iLandscape" src="....." :width="myDimension">
<img v-else src="....." height="myDimension">

Repeat with v-for

Using an array as your data source, each element in the array can be bound using the v-for directive.

TIP

For performance reasons, VueJS requires elements generated by v-for be assigned a unique key among the siblings of the same parent. When the array elements are unique, you can use the element itself as a key. Otherwise you can use the element position as the key.

Simple Loop

vue
<template>
  <div id="app">
    List of atom names
    <ul>
      <li v-for="a in atoms">{{a}}</li>
    </ul>

    List of atom names (with item as key):
    <ul>
      <li v-for="b in atoms" :key="b">{{b}}</li>
    </ul>

    List of atom names (with position as key):
    <ul>
      <li v-for="(c,pos) in atoms" :key="pos">{{c}}</li>
    </ul>
  </div>
</template>
  
<script>
export default {
  data: () => {
    return {
      atoms: ["Argon", "Carbon", "Neon"]
    };
  }
};
</script>

Iterate Through Array of Objects

When using v-for with an array of objects, the key is usually a unique value from the array element or the element position.

vue
<template>
  <div id="app">
    <ul>
      <li v-for="a in atoms" :key="a.symbol">{{a.name}} ({{a.symbol}})</li>
    </ul>

  </div>
</template>

<script>
export default {
  data: () => {
    return {
      atoms: [
        {
          name: "Argon",
          symbol: "Ar",
          weight: 39.8
        },
        {
          name: "Gold",
          symbol: "Au",
          weight: 196.97
        }
      ]
    };
  }
};
</script>

Loop with <template>

In some cases, you must loop over a several HTML elements as a group. Consider the following example where each group consists of one <em>, one <i>, and text nodes:

html
<em>Gold</em> (<i>Au</i>)
<em>Silver</em> (<i>Ag</i>)

One solution is to wrap each group in a <span> or <div> but this will create a deeper nesting of the elements.

html
<span v-for="(a,pos) in atoms" :key="pos">
  <em>{{a.name}}</em> (<i>{{a.symbol}}</i>)
</span>

If deeper nesting is not preferred, you can wrap each group in a <template>. Using this approach, the key must be assigned to the "real" elements:

html
<template v-for="(a,pos) in atoms">
  <em :key="`n-${pos}`">{{a.name}}</em> (<i :key="`s-${pos}`">{{a.symbol}}</i>)
</template>

TIP

In the above example, we can't use :key="pos" because that creates duplicate key (one for <em> and one for <i>). To avoid this issue, we use unique strings to distinguish the two: n- and s-. So the keys are : n-0, s-0, n-1, s-1, etc.

vue
<template>
  <div id="app">

    <template v-for="(a,pos) in atoms">
      <b v-bind:key="`name-${pos}`">{{a.name}}</b> (<i
        :key="`sym-${pos}`">{{a.symbol}}</i>)
    </template>

    <table border="1" cellspacing="0" cellpadding="8">
      <thead>
        <tr>
          <th>Symbol</th>
          <th>Name</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(b,pos) in atoms" v-bind:key="pos">
          <td>{{b.symbol}}</td>
          <td>{{b.name}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      atoms: [{ name: "Silver", symbol: "Ag" }, { name: "Gold", symbol: "Au" }]
    };
  }
};
</script>

Conditional

Use v-if, v-else-if, and v-else directives to conditionally include different parts of HTML sections/groups based on specific conditions of your data. For instance, you may have conditional statements below:

java
if (fuel > 11) {
  show ("Plenty of fuel!")
} else if (fuel >= 3 && fuel <= 11) {
  show ("Enough to drive....")
} else if() {
  show ("....");
} else {
  show("Run out of gas!");
}

which translates to VueJS v-if directives below:

vue
<template>
  <div id="app">
    <h2>Fuel Gauge</h2>

    <p>You have {{fuel}} gallon(s) of fuel</p>

    <b v-if="fuel > 11">Plenty of fuel!</b>
    <b v-else-if="fuel >= 3 && fuel <= 11">Enough to drive {{fuel * mpg}}
      miles</b>
    <div v-else-if="fuel > 0">
      <h3>WARNING</h3>
      <p>Find nearby gas station within {{fuel * mpg}} miles</p>
    </div>
    <b v-else>Oops! You ran out of gas</b>
  </div>
</template>

<script>
export default {
  data: () => {
    return {
      fuel: 2.1,
      mpg: 23.5
    };
  }
};
</script>

Binding

Attribute Binding

vue
<template>
  <div id="app">
    <ul>
      <li v-for="b in birds">
        {{b.name}}
        <img v-bind:src="b.imageURL" width="300">
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      birds: [
        {
          name: "Robin",
          imageURL:
            "https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
        },
        {
          name: "Cardinal",
          imageURL:
            "https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg"
        }
      ]
    };
  }
};
</script>

Style Binding

Event Handling

vue
<template>
  <div id="app">
    <p>Move the mouse over each text</p>
    <ul>
      <li
        v-for="b in birds"
        v-on:mouseover="updateImage(b.imageURL)"
        @mouseleave="resetImage"
      >{{b.name}}</li>
    </ul>

    <img v-bind:src="currentImage" width="200">
  </div>
</template>

<script>
export default {
  data() {
    return {
      birds: [
        {
          name: "Robin",
          imageURL:
            "https://upload.wikimedia.org/wikipedia/commons/c/c6/American_Robin_2006.jpg"
        },
        {
          name: "Cardinal",
          imageURL:
            "https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg"
        }
      ],
      currentImage: ""
    };
  },
  methods: {
    updateImage(url) {
      this.currentImage = url;
    },
    resetImage: function() {
      this.currentImage = "";
    }
  }
};
</script>
<style scoped>
li {
  display: inline;
  padding: 0 1em;
}

li:hover {
  border-bottom: 2px solid rgb(42, 165, 93);
}
</style>
vue
<template>
  <div id="app">
    <p>Press Shift-Click on the image</p>
    <p ref="msg" class="fadeout" v-show="msg.length > 0">{{msg}}</p>
    <div @click.shift="showText">
      <img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Cardinal.jpg" width="600">
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: ""
    };
  },
  methods: {
    showText() {
      this.msg = "Yes";
      // Auomatically reset text after 2 seconds
      setTimeout(() => {
        this.msg = "";
      }, 2000);
    }
  }
};
</script>

<style scoped>
.fadeout {
  animation-duration: 2s;
  animation-name: fadingout;
  opacity: 0;
}

@keyframes fadingout {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
</style>

2-way Binding

vue
  <style scoped>
#inputBox {
  display: grid;
  grid-template-columns: 100px 10em;
  grid-template-rows: auto auto;
}
</style>

<template>
  <div id="app">
    <h2>Atoms</h2>
    <p>Your input: {{atoms[selectedAtom]}} Weight: {{atomWeight}}</p>
    <select v-model="selectedAtom">
      <template v-for="(name,pos) in atoms">
        <option :value="pos">{{name}}</option>
      </template>
    </select>
    <div id="inputBox">
      <span>Name</span>
      <input type="text" v-model="atoms[selectedAtom]">
      <span>Weight</span>
      <input type="text" v-model.lazy.number="atomWeight">
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      atoms: [
        "Hydrogen",
        "Beryllium",
        "Boron",
        "Carbon",
        "Nitrogen",
        "Oxygen",
        "Fluorine",
        "Helium"
      ],
      atomName: "",
      atomWeight: 0,
      selectedAtom: 0
    };
  }
};
</script>

Life cycle Functions

vue

<template>
  <div id="app">
    Invoked callbacks:
    <ul>
      <li v-for="m in callbacks">{{m}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      callbacks: []
    };
  },
  methods: {},
  // beforeCreate() {
  //   // this.callbacks.push("beforeCreate()");
  // },
  created() {
    this.callbacks.push("created()");
  },
  beforeMount() {
    this.callbacks.push("beforeMount()");
  },
  mounted() {
    this.callbacks.push("mounted()");
  },
  beforeUpdate() {
    //   // this.callbacks.push("beforeUpdate()");
    console.log("beforeUpdate");
  },
  updated() {
    // this.callbacks.push("updated()");
    console.log("updated");
  },
  beforeDestroy() {
    console.log("beforeDestroy()");
  },
  destroyed() {
    console.log("destroyed()");
  }
};
</script>