Categories
Vue 3

Create Vue 3 Apps with the Composition API — Lifecycle Hooks and Refs

It lets us extract logic easily an not have to worry about the value of this in our code.

It also works better with TypeScript because the value of this no longer has to be typed.

In this article, we’ll look at how to create Vue 3 apps with the Composition API.

Lifecycle Hooks

Lifecycle hooks are also available with Vue 3’s composition API.

We put them all in the setup method.

For instance, we can write:

<template>
  <div></div>
</template>
@click
<script>
import { onMounted, onUnmounted, onUpdated } from "vue";
export default {
  name: "App",
  setup() {
    onMounted(() => {
      console.log("mounted!");
    });
    onUpdated(() => {
      console.log("updated!");
    });
    onUnmounted(() => {
      console.log("unmounted!");
    });
  },
};
</script>

We pass in the callback into the lifecycle functions.

beforeCreate and created are replaced with the setup method.

And the following are the composition API equivalents of the options API hooks:

  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • activated -> onActivated
  • deactivated -> onDeactivated
  • errorCaptured -> onErrorCaptured

The left side has the options API hooks and the right side has the composition API equivalents.

Composition API also have 2 debug hooks:

  • onRenderTracked
  • onRenderTriggered

We can write:

export default {
  onRenderTriggered(e) {
    debugger
  }
}

to inspect which dependency is causing the component to re-render.

Dependency Injection

We can inject dependencies with the provide and inject functions.

provide and inject enables dependency injection similar to project and inject in Vue 2.x.

For instance, we can write:

<template>
  <div></div>
</template>
@click
<script>
import { provide, inject } from "vue";
@click
const ThemeSymbol = Symbol();
@click
const Ancestor = {
  setup() {
    provide(ThemeSymbol, "dark");
  },
};
@click
export default {
  name: "App",
  setup() {
    const theme = inject(ThemeSymbol, "light");
    return {
      theme,
    };
  },
};
</script>

We have the Ancestor component, which calls provide to let us pass the dependency identified with ThemeSymbol down to the App component.

In App ‘s setup method, we call inject with ThemeSymbol to get the data passed in from provide .

'light' is the default value if nothing with the identifier ThemeSymbol is provided.

Template Refs

Composition API lets us access template refs.

We use the ref function to create the ref.

Then we can assign it in the template to the element or component we want.

For instance, we can write:

<template>
  <div ref="root"></div>
</template>
@click
<script>
import { ref, onMounted } from "vue";
@click
export default {
  name: "App",
  setup() {
    const root = ref(null);
@click
    onMounted(() => {
      console.log(root.value);
    });
@click
    return {
      root,
    };
  },
};
</script>

We create the root ref with the ref function.

Then we set the name of the ref to the ref attribute.

Then root.value would be the div.

If we use a render function, we write:

<template>
  <div></div>
</template>
@click
<script>
import { ref, onMounted, h } from "vue";
@click
export default {
  name: "App",
  setup() {
    const root = ref(null);
@click
    onMounted(() => {
      console.log(root.value);
    });
@click
    return () =>
      h("div", {
        ref: root,
      });
  },
};
</script>

to assign the ref to the div we create in the h function.

Conclusion

We can add lifecycle hooks and refs into our Vue 3 app with the composition API.

Categories
Vue 3

Create Vue 3 Apps with the Composition API — Watch and Watch Effect

Vue 3 comes with the Composition API built-in.

It lets us extract logic easily an not have to worry about the value of this in our code.

It also works better with TypeScript because the value of this no longer has to be typed.

In this article, we’ll look at how to create Vue 3 apps with the Composition API.

watch

The watch function in the Vue 3 composition API is the same as Vue 2’s this.$watch method or the watch option.

Therefore, we can use it to watch for changes in reactive properties.

For instance, we can write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ state.count }}
  </div>
</template>

<script>
import { reactive, watch } from "vue";
export default {
  name: "App",
  setup() {
    const state = reactive({ count: 0 });

    const increment = () => {
      state.count++;
    };
    watch(
      () => state.count,
      (count, prevCount) => {
        console.log(count, prevCount);
      }
    );

    return {
      state,
      increment,
    };
  },
};
</script>

We watch a getter function in the 2nd argument.

And we get the current and previous value in the first and 2nd parameter of the function we pass into watch as the 2nd argument.

Now when we click on the increment button, we see state.count increase.

If we have a primitive valued reactive property, we can pass it straight into the first argument of watch :

<template>
  <div>
    <button @click="increment">increment</button>
    {{ count }}
  </div>
</template>

<script>
import { ref, watch } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    const increment = () => {
      count.value++;
    };
    watch(count, (count, prevCount) => {
      console.log(count, prevCount);
    });

    return {
      count,
      increment,
    };
  },
};
</script>

And we get the same values we for count and prevCount when we click on the increment button.

Watching Multiple Sources

We can also watch multiple refs.

For instance, we can write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ foo }}
    {{ bar }}
  </div>
</template>

<script>
import { ref, watch } from "vue";
export default {
  name: "App",
  setup() {
    const foo = ref(0);
    const bar = ref(0);
    const increment = () => {
      foo.value++;
      bar.value++;
    };
    watch([foo, bar], ([foo, bar], [prevFoo, prevBar]) => {
      console.log([foo, bar], [prevFoo, prevBar]);
    });

    return {
      foo,
      bar,
      increment,
    };
  },
};
</script>

We pass in the foo and bar refs into the array.

Then we get the current and previous values from the arrays in the parameters of the function in the 2nd argument.

We can also pass in the onInvalidate function into the 3rd argument.

And other behaviors are also shared with watchEffect .

Conclusion

We can watch reactive properties with Vue 3’s composition API watchers.

Categories
Vue 3

Create Vue 3 Apps with the Composition API — Read-Only Properties and Side Effects

Vue 3 comes with the Composition API built-in.

It lets us extract logic easily an not have to worry about the value of this in our code.

It also works better with TypeScript because the value of this no longer has to be typed.

In this article, we’ll look at how to create Vue 3 apps with the Composition API.

Read-Only Property

We can add a read-only property to our Vue 3 app with the composition API.

To add it, we use the readonly property:

<template>
  <div>{{ copy }}</div>
</template>

<script>
import { reactive, readonly } from "vue";
export default {
  name: "App",
  setup() {
    const original = reactive({ count: 0 });
    const copy = readonly(original);
    return {
      copy,
    };
  },
};
</script>

We define the original reactive property with reactive .

Then we call readonly with original to create a read-only deep copy of the original.

And we return it, so we can use it.

Watch Reactive Properties

We can watch reactive properties with the watchEffect method.

For instance, we can write:

<template>
  <div>{{ count }}</div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    watchEffect(() => console.log(count.value));

    setTimeout(() => {
      count.value++;
    }, 100);

    return {
      count,
    };
  },
};
</script>

We call watchEffect with a callback to log the value of count when it’s updated in the setTimeout callback.

watchEffect returns a function that we can use to stop the watcher.

To use it, we write:

<template>
  <div>{{ count }}</div>
</template>

<script>
import { onBeforeUnmount, ref, watchEffect } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    const stop = watchEffect(() => console.log(count.value));

    setTimeout(() => {
      count.value++;
    }, 100);

    onBeforeUnmount(() => stop());

    return {
      count,
    };
  },
};
</script>

We call stop in the onBeforeUnmount callback to stop the watcher when we’re unmounting the component.

Also, we can invalidate side effects with the onInvalidate function.

For instance, we can write:

<template>
  <div>{{ size }}</div>
</template>

<script>
import { onBeforeMount, reactive, watchEffect } from "vue";
export default {
  name: "App",
  setup() {
    const size = reactive({
      width: 0,
      height: 0,
    });

    const onResize = () => {
      size.width = window.innerWidth;
      size.height = window.innerHeight;
    };
    onBeforeMount(() => window.addEventListener("resize", onResize));

    watchEffect((onInvalidate) => {
      onInvalidate(() => {
        window.removeEventListener("resize", onResize);
      });
    });

    return {
      size,
    };
  },
};
</script>

to call window.removeEventListener to remove the event listener in the onInvalidate callback.

The onResize function sets the size when we change the screen by attaching it as the listener for the resize event.

Conclusion

We can add read-only properties and watch side effects with Vue 3’s composition API.

Categories
Vue 3

Create Vue 3 Apps with the Composition API

Vue 3 comes with the Composition API built-in.

It lets us extract logic easily an not have to worry about the value of this in our code.

It also works better with TypeScript because the value of this no longer has to be typed.

In this article, we’ll look at how to create Vue 3 apps with the Composition API.

Basic Example

We can create a basic app by defining reactive properties and using them in templates.

For instance, we can write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ count }}
  </div>
</template>

<script>
import { ref } from "vue";

export default {
  setup() {
    const count = ref(0);

    function increment() {
      count.value++;
    }

    return {
      count,
      increment,
    };
  },
};
</script>

We define the count reactive property with the ref function.

0 is its initial value.

And we add the increment function to update its value.

It’s updated differently than in the options API. We’ve to update the value property to update a reactive property with primitive values.

Then we return both count and increment so we can use them our template.

setup is a method that runs when we mount the component.

We can define object valued reactive properties with the reactive function.

To do this, we write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ state.count }}
  </div>
</template>

<script>
import { reactive } from "vue";

export default {
  setup() {
    const state = reactive({
      count: 0,
    });
    function increment() {
      state.count++;
    }

    return {
      state,
      increment,
    };
  },
};
</script>

We call reactive with an initial object value.

Then we assign it to the state reactive property.

In the increment function, we update the state.count property to update its value.

And we return state and count so we can use them in the template.

Computed Property

To create a computed property, we can use the computed function.

To do this, we write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ state.count }}
    {{ double }}
  </div>
</template>

<script>
import { reactive, computed } from "vue";

export default {
  setup() {
    const state = reactive({
      count: 0,
    });
    const double = computed(() => state.count * 2);

    function increment() {
      state.count++;
    }

    return {
      state,
      double,
      increment,
    };
  },
};
</script>

We pass a callback into the computed method to return the value we want for the computed property.

computed can be called in the object that we pass into reactive to add the computed property as a property of another reactive property.

Watchers

We can add a watcher into our Vue app with the watch function.

For instance, we can write:

<template>
  <div>
    <button @click="increment">increment</button>
    {{ state.count }}
  </div>
</template>

<script>
import { reactive, watch } from "vue";

export default {
  setup() {
    const state = reactive({
      count: 0,
    });
    function increment() {
      state.count++;
    }
    watch(
      () => state.count,
      (count) => {
        console.log(count);
      },
      { immediate: true }
    );
    return {
      state,
      increment,
    };
  },
};
</script>

to add it.

The first argument of watch is a function that returns the state.count reactive property.

In the 2nd argument, we get the latest value of state.count with count and log it.

The 3rd argument is an object with options for the watcher.

We can set deep and immediate in there as we do with the options API.

deep means watch all nested properties of an object for changes.

immediate means the watcher runs immediately when the component is mounted.

Conclusion

We can use the Vue 3 Composition API to define our components.

All the things we have in the options API are still available with some improvements.

Categories
PrimeVue

Vue 3 Development with the PrimeVue Framework — Toggle and Reorder Table Columns

PrimeVue is a UI framework that’s compatible with Vue 3.

In this article, we’ll look at how to get started with developing Vue 3 apps with PrimeVue.

Toggle Table Columns

We can add a multi-select dropdown to let us toggle table columns on and off.

For instance, we can write:

<template>
  <div>
    <DataTable :value="cars">
      <template #header>
        <div style="text-align: left">
          <MultiSelect
            :modelValue="selectedColumns"
            :options="columns"
            optionLabel="header"
            @update:modelValue="onToggle"
            placeholder="Select Columns"
            style="width: 20em"
          />
        </div>
      </template>
      <Column
        v-for="(col, index) of selectedColumns"
        :field="col.field"
        :header="col.header"
        :key="`${col.field}_${index}`"
      ></Column>
    </DataTable>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      cars: [
        { brand: "Volkswagen", year: 2012, color: "Orange", vin: "dsad231ff" },
        { brand: "Audi", year: 2011, color: "Black", vin: "gwregre345" },
        { brand: "Renault", year: 2005, color: "Gray", vin: "h354htr" },
      ],
      selectedColumns: [],
      columns: [
        { field: "vin", header: "VIN" },
        { field: "year", header: "Year" },
        { field: "color", header: "Color" },
        { field: "brand", header: "Brand" },
      ],
    };
  },
  methods: {
    onToggle(value) {
      this.selectedColumns = this.columns.filter((col) => value.includes(col));
    },
  },
  created() {
    this.selectedColumns = this.columns;
  },
};
</script>

We listen to the update:modelValue event to run code to filter out the columns that we disabled.

We get the selected columns from the value parameter.

Resizable Columns

We can make columns resizable with the resizableColumns prop:

<template>
  <div>
    <DataTable
      :value="cars"
      :resizableColumns="true"
      columnResizeMode="fit"
      class="p-datatable-gridlines"
    >
      <Column field="vin" header="Vin"> </Column>
      <Column field="year" header="Year"> </Column>
      <Column field="brand" header="Brand"></Column>
      <Column field="color" header="Color"></Column>
    </DataTable>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      cars: [
        { brand: "Volkswagen", year: 2012, color: "Orange", vin: "dsad231ff" },
        { brand: "Audi", year: 2011, color: "Black", vin: "gwregre345" },
        { brand: "Renault", year: 2005, color: "Gray", vin: "h354htr" },
      ],
    };
  },
  methods: {},
};
</script>

We set the prop to true to enable column resizing.

columnResizeMode is set to 'fit' to make the columns fit the content.

Reordering Table Rows

Table rows can be reordered by listening to the row-reorder event:

<template>
  <div>
    <DataTable
      :value="cars"
      :reorderableColumns="true"
      @column-reorder="onColReorder"
      @row-reorder="onRowReorder"
    >
      <Column
        :rowReorder="true"
        headerStyle="width: 3rem"
        :reorderableColumn="false"
      />
      <Column
        v-for="col of columns"
        :field="col.field"
        :header="col.header"
        :key="col.field"
      ></Column>
    </DataTable>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      cars: [
        { brand: "Volkswagen", year: 2012, color: "Orange", vin: "dsad231ff" },
        { brand: "Audi", year: 2011, color: "Black", vin: "gwregre345" },
        { brand: "Renault", year: 2005, color: "Gray", vin: "h354htr" },
      ],
      columns: [
        { field: "vin", header: "VIN" },
        { field: "year", header: "Year" },
        { field: "color", header: "Color" },
        { field: "brand", header: "Brand" },
      ],
    };
  },
  methods: {
    onColReorder() {},
    onRowReorder(event) {
      this.cars = event.value;
    },
  },
};
</script>

We get the reordered items with the event.value property.

Likewise, we can listen to the column-reorder event to listen for column reordering actions.

Conclusion

We can toggle and reorder table rows and columns withn PrimeVue’s table.