w3resource
Vue Tutorial

Handling Edge Cases

This tutorial documents the handling of edge cases, this means the unusual situations that sometimes require bending of Vue's rules a little. However, these edge cases all have disadvantages or situations where they can be dangerous.

Element & Component Access

It is best to avoid reaching into other component instances or manually manipulating DOM elements in most cases. However, there are cases when it can be appropriate.

Accessing the Root Instance

The root instance can be accessed with the $root property in every subcomponent of the a new Vue instance:

new Vue({
  data: {
    foo: 1
  	}
       })

Then all the subcomponents can access the instance and use it as a global store. This is also applicable for computed properties and methods.

this.$root.foo

However, it is recommended that we use Vuex to manage state changes.

Accessing the parent Component Instance

Just like $root, the $parent property can be used to access the parent instance from a child. This is a lazy alternative to using props to pass data.

However, in cases where we have shared libraries this could be appropriate. For instance, where abstract components interact with JavaScript APIs rather than rendering HTML:

<google-map>
  <google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map>

The <google-map> component may define a map property that all subcomponents have access to. The <google-map-markers> may want to access that map using this.$parent.getMap, so as to add a set of markers to it. It is recommended that we use dependency injection instead.

Accessing Child Component Instances & Child Elements

There are sometimes when we need to access a child component directly in JavaScript, despite the existence of events and props. This is achieved using a reference ID to the child component using the ref attribute.

And then in the component where we have defined this ref, we can use:

this.$refs.usernameInput

to access the instance of <base-input>.

Dependency Injection

Consider the code snippet below:

<google-map>
  <google-map-region v-bind:shape="cityBoundaries">
    <google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
  </google-map-region>
</google-map>

Where all the descendant of needs access to a getMap method, so as to know which map to interact with.

Using the $parent property does not scale well for more deeply nested components, hence we need dependency injection. This uses two new instance options: reject and provide.

The provide options will allow us to specify the data/method we want to provide to the descendant components. In our case the method we want to provide is getMap inside<google-map>:

Now in any descendants, we can use the inject option to receive specific properties that we will like to add to the interface:

inject: ['getMap']

Programmatic Event Listeners

Thus far, we have listened with the v-on directive, we have seen uses of $emit, but Vue instances offer more methods than these in its events interface. we can then:

  • Listen for an event using $on(eventName,eventHandler)
  • Listen for an event only once using $once(eventName,eventHandler)
  • Stop listening for an event using $off(eventName eventHandler)

These are available for cases when we need to manually listen for events on a component instance as in the case below:

mounted: function () {
  this.picker = new Pikaday({
    field: this.$refs.input,
    format: 'YYYY-MM-DD'
  })
},
beforeDestroy: function () {
  this.picker.destroy()
}

There are two potential issues associated with this:

  • It will require saving the picker to the component instance, even though it's possible that only lifecycle hooks need access to it. This isn't so terrible, but it could however be considered clutter.
  • Our setup code will be kept separate from our cleanup code, thus making it more difficult to programmatically clean up anything we set up.

Both are resolved using a programmatic listener.

Circular References

Recursive Components

It is possible for components to invoke themselves in their own template recursively. But, this can only be done with the name option:

name: 'unique-name-of-my-component'

When we register a component globally using Vue.component, the global id will be automatically set as the component's name option. If we are not careful, the recursive components could also lead to infinite lops:

name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'

Always make sure that recursive invocation is conditional to avoid "max stack size exceeded error".

Circular References Between Components

Take the case scenario where you are building a file directory tree (file explorer). You may have a tree-folder component with this template:

<p>
  <span>{{ folder.name }}</span>
  <tree-folder-contents :children="folder.children"/>
</p>

And a tree-folder-contents component with the template above:

<ul>
  <li v-for="child in children">
    <tree-folder v-if="child.children" :folder="child"/>
    <span v-else>{{ child.name }}</span>
  </li>
</ul>

Looking closely, we can see that these components will be each other's descendant and ancestor I the render tree-this is a paradox which is automatically resolved when registering the components globally with Vue.component. However, if we are requiring/importing components using a module system, we'll get an error:

Failed to mount component: template or render function not defined.

To resolve the error, we can use Webpack's asynchronous import when registering the components locally:

components: {
  TreeFolderContents: () => import('./tree-folder-contents.vue')
}

Or wait until beforCreate lifecycle hook to register the tree-folder-contents component:

Alternate Template Definitions

Inline Templates

Whenever the inline-template special attribute is present on a child component, that component will use its inner content as its template, instead of treating it as a distributed content.

X-Templates

An alternative way to define templates is to define them inside a script element with the type text/x-template, and then referencing the template by an id.

Controlling Updates

With Vue's Reactivity system, Vue knows when to update (when used correctly). Though there are time you will want to force an update.

Forcing an Update

Note: 99.99% of the times when you feel you need to force an update; you have made a mistake somewhere.

But if you are certain you didn't make an error, Then you can force updates with $forceUpdate.

Cheap Static Components with v-once

When we render HTML in Vue, it is very fast, but there are times when we have components which have lots of static contents. We can ensure that this is evaluated once and then cached. This is done by adding the v-once directive to the root elemet.