diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Vue + TypeScript App architecture with autonomous service layer implementation

Goal: having an autonomous, central layer for the different services defined in their own modules that will provide all registered services through a custom hook

Step 1: in a new TypeScript file, import all needed services from their modules

import { Service1 } from '@/location1/Service1'
import { Service2 } from '@/location2/Service2'
import { Service3 } from '@/location3/Service3'

Step 2: define the object containing the services, so that we will be able to retrieve the services from it. New services will be imported and registered in this object. After that, extract its type for further need.

const services = {
   service1: Service1,
   service2: Service2
   service3: Service3,
};

type AvailableServices = typeof services

Step 3: define a ServiceProvider class that will internally hold the services object

class ServiceProvider {
  constructor(protected services: AvailableServices) {}

  public provide( serviceName: keyof AvailableServices)
  :InstanceType<AvailableServices[keyof AvailableServices]> {
    return new this.services[serviceName]();
  }
}

Note 1: constructor automatically initialising protected class member "services", no explicit initialiser needed

Note 2: the generic return type of the provide method. It creates the returned type instance based on the constructor function of the service returned

Step 4: create a service provider instance

const serviceProvider = new ServiceProvider(services);

Step 5: define the magic hook that we will be calling from all components for making use of the above defined logic

export function useService( serviceName: keyof AvailableServices,)
:InstanceType<AvailableServices[keyof AvailableServices]> {
  return serviceProvider.provide(serviceName);
}

How to use it inside components:

import { useService } from '@/itsLocation';

const service1 = useService('service1');

Nice to have as a future implementation: each service as a singleton

Customize web page using query parameters with help of $route.query

VueJS offers the possibility to access query parameters so that you can customise a certain's page content based on the given parameters.

For instance, we have the following method and the following query parameters:

https://*link*?message=Custom+Message
    getQuery() {
      if (this.$route.query.message) {
        this.queryMessage = this.$route.query.message
      }
    },

This method will set a variable according to the query parameter "message", that can be further used in the content of the page. For example in a heading:

    <h1>{{ queryMessage }}</h1>

Also, by using the functionality of $router combined with "v-if", the user can render components conditionally.

    getQuery() {
      if (this.$route.query.icon) {
        this.queryIcon = this.$route.query.icon === 'true'
      }
    },
    <svg v-if="queryIcon">

Therefore, when parsing a query containing:

?icon=true

only then, the svg will be rendered.

How to access refs outside a VueJs template

Sometimes you need to make use of the ref attribute to access a child component in VueJs, like so:

<input ref="someRefName"></input>
this.$refs.someRefName

But what if you need to do the same thing for an html element that's still inside your app but outside of your VueJs template or component? Maybe you've tried it and this.$refs.someRefName comes in undefined.

That's when the $root instance comes in handy. Just add the ref attribute on the target element and access it in your VueJs component like this:

this.$root.$refs.someRefName

That makes VueJs look for that reference in the root instance of your app, and not only inside your component.

Just make sure you don't overuse it. Most of the times, there's a better way to do what you need to do.