Creating Custom HTML Components Using Angular Directives

Creating custom HTML components

Image By Andrian Valeanu on Unsplash

Posted on

I am working on a project using the VueJS Javascript framework (If you have not checked out the framework, I highly recommend it, especially for those of you familiar with AngularJs). The project involves having a list of objects, upon which I would need to add/remove properties.

I quickly discovered that adding new properties to objects does not trigger updates to watchers, data bindings, or computed properties. This quickly became a headache for me, and after many hours of experimenting and googling, I found the fix. I will share that with you here.

A Quick Demo

Below is a very simplified version of what I was trying to do. I had a Vue instance that contained a list of objects. Those objects were passed to a component that formatted them nicely into a list/table. Upon clicking on an item in the component, the parent list would need to be updated, which should trigger updates within the component itself. Here is a fiddle of the code: https://jsfiddle.net/pstephan1187/o0bsbdxd/

The HTML:

<div id="el">
    <people :items="people" @selectperson="handlePersonSelection"></people>

    <div>
        {{people | json}}
    </div>
</div>

<template id="component-template">
    <div>
        <div v-for="person in items" class="person" @click="handleClick(person)">
            {{person | json}}
        </div>
    </div>
</template>

The Javascript:

var Component = Vue.extend({
    template: '#component-template',
    props: ['items'],
    methods: {
        handleClick: function(person){
            this.$dispatch('selectperson', person);
        }
    }
});

new Vue({
    el: '#el',
    data: {
        people: [
            {name: "Fred"},
            {name: "Amelia"},
            {name: "Gerald"},
            {name: "Victor"},
            {name: "Ashley"},
            {name: "Herold"},
            {name: "Karissa"}
        ]
    },
    methods: {
        handlePersonSelection: function(person){
            person.age = 20;
        }
    },
    components: {
        people: Component
    }
});

The way this is supposed to work is as follows:

  1. The component list items detect a click with triggers the handleClick method.
  2. The component dispatches a selectperson event which is registered to the parent's handlePersonSelection method on the component: <people ... @selectperson="handlePersonSelection"></people>.
  3. The handlePersonSelection is supposed to add the new age property to the represented object.
  4. The change is supposed to show in the both the list and in the JSON dump.

This does not work as you can see in the fiddle. The reason is because, due to a javascript limitation, Vue cannot detect the addition of new properties on object. To overcome this, Vue provides a method to add properties to object that trigger updates: Vue.set().

The Solution

Vue.set() has three arguments: the object you want to add the property to, the name of the new property, and the value of the new property. So to fix the entire problem, all I had to do was change the person.age = 20; to Vue.set(person, 'age', 20); and viola!

Here is a fiddle of the working code: https://jsfiddle.net/pstephan1187/68fs48j2/1/

And the working Javascript:

var Component = Vue.extend({
    template: '#component-template',
    props: ['items'],
    methods: {
        handleClick: function(person){
            this.$dispatch('selectperson', person);
        }
    }
});

new Vue({
    el: '#el',
    data: {
        people: [
            {name: "Fred"},
            {name: "Amelia"},
            {name: "Gerald"},
            {name: "Victor"},
            {name: "Ashley"},
            {name: "Herold"},
            {name: "Karissa"}
        ]
    },
    methods: {
        handlePersonSelection: function(person){
            Vue.set(person, 'age', 20);
        }
    },
    components: {
        people: Component
    }
});

You can read more about VueJS and reactivity here: https://vuejs.org/guide/reactivity.html. I hope this saves you a lot of time.

Newsletter


Get notified when I post something new.

Latest Quick Tips


Bash Alias for Creating a Laravel Project
  • A Quick Tip posted on 2018-07-31
Carbon Helper
  • A Quick Tip posted on 2017-10-05

Latest Blog Posts


Custom Tinkerwell Integration
  • A Blog Post posted on 2019-11-05
How to Persist Databases Across Homestead Instances
  • A Blog Post posted on 2018-02-16
How to Set Up Xdebug on Laravel Homestead and VSCode
  • A Blog Post posted on 2017-10-23
Compiling LESS Using Blade Templates
  • A Blog Post posted on 2016-07-21