diceline-chartmagnifiermouse-upquestion-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.

Conditionally add properties to objects in JavaScript

This comes in handy when you want to completely omit a property from an object in case the value is undefined or null.

{prop1, prop2, ...(condition && prop3)}

The trick being here:

...(condition && prop3)

In case the condition evaluates to false, the property will be completely omitted from the object, otherwise, both the value and its corresponding key will show up in the end result.

Correctly grouping members within modules in JSDoc v3

JSDoc is great! It's documentation not so much. I needed a way to logically group together global methods and I was unsuccessful in finding a straightforward way in its official documentation.

Eventually, I've come across the following method, using @module and @member.

First, we define the module name, usually at the top of the file:

@module MyModule

We then proceed to annotate the target function with @memeber MyModule. One other important aspect is to also include @function along with the name of the function in order to appear in the module section.

/**
 * @member MyModule
 * @function myFunction
 ....
 */
 export const myFunction = () => ....

How to set srcset and sizes attributes for responsive images

The default src attribute acts as fallback for a default image size for browsers that have no support for srcset:

<img src="/images/image.jpg">

The srcset attribute gives the browser a bunch of different sizes of the same image to choose from.

The name of the image doesn't matter, you could name them image-small, image-medium and so on, but the 375w part should match the real width of the image:

<img
    src="/images/image.jpg"
    srcset="/images/image-375.jpg 375w,
            /images/image-768.jpg 768w,
            /images/image-1440.jpg 1440w,
            /images/image-1920.jpg 1920w,
            /images/image-2560.jpg 2560w"
>

Adding the sizes attribute specifies what image size to choose at a specific breakpoint and above (min-width) or below (max-width) it:

<img
    src="/images/image.jpg"
    srcset="/images/image-375.jpg 375w,
            /images/image-768.jpg 768w,
            /images/image-1440.jpg 1440w,
            /images/image-1920.jpg 1920w,
            /images/image-2560.jpg 2560w"
    sizes="(min-width: 1920px) 1400px, (min-width: 1600px) 1000px, (min-width: 768px) 700px, 300px"
>

One thing to note here is that the browser will first calculate the device width and then look at the sizes attribute and choose the first matching condition.

If you set for example sizes="(max-width: 1920px) 1500px, (max-width: 768px) 600px, 300px", and the device width is 640px, the first matching condition will be max-width: 1920px, therefore it will choose the 1500px image width suggestion, rather than the 600 you most probably expected. The same can happen with the min-width if the order is not right.

Notice also that we are asking the browser to choose a 1000px wide image between 1600px and 1919px, but in the srcset sources, there's no image at 1000px. Here, the browser will choose the image with the exact size, if there is one, or the closest one if not, but higher in size than specified, in this case, 1000px. Therefore, the 1440px will be selected.

It can take a while to grasp how this works, but it's worth taking the time to understand it.

Postman Automatic Auth Token

Postman can run JavaScript commands when executing a request. These commands can be placed in the "Tests" tab inside a request. For example, to automatically set a token as a collection variable, the following commands are used:

var auth = pm.response.headers.get("Authorization");
pm.collectionVariables.set('token', auth.split(" ")[1]);

The Docs regarding Postman scripting can be found here:

https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/