[Vuejs]-Dynamically populate object values with function calls

0👍

This line seems potentially wrong:

const methodName = `get${captureType.charAt(0).toUpperCase() + captureType.slice(1)}()`;

So if captureType is voice then methodName will be getVoice(). I don’t think you want the () on the end.

Then there’s this:

bioTypes[type.bioType] = () => this[methodName];

It becomes clearer what the problem is if we write it out in full:

bioTypes[type.bioType] = function () {
  // Ignoring the scoping issue for a moment...
  return this[methodName];
}

The function is returning your method rather than invoking it.

Instead you want:

bioTypes[type.bioType] = () => this[methodName]();

Here’s a complete example with those mistakes corrected:

BiometricService = {
  async fetchAll () {
    return {
      data: [
        { bioType: 40, captureType: 'face' },
        { bioType: 41, captureType: 'face' },
        { bioType: 42, captureType: 'face' },
        { bioType: 43, captureType: 'face' },
        { bioType: 60, captureType: 'passphrase' },
        { bioType: 61, captureType: 'passphrase' },
        { bioType: 140, captureType: 'pin' },
        { bioType: 141, captureType: 'pin' },
        { bioType: 150, captureType: 'palm' },
        { bioType: 152, captureType: 'palm' }
      ]
    };
  }
};

class BiometricMap {
  static async get(bioType) {
    if (!bioType) {
      return BiometricMap.default();
    }

    const bioTypes = {};
    const baseBioTypes = await BiometricService.fetchAll();

    baseBioTypes.data.forEach((type) => {
      // Yet another place we have to convert 'passphrase' to 'voice'.
      const captureType = type.captureType === 'passphrase' ? 'voice' : type.captureType;
      const methodName = `get${captureType.charAt(0).toUpperCase()}${captureType.slice(1)}`;
      bioTypes[type.bioType] = () => this[methodName]();
    });

    return (bioTypes[bioType])();
  }

  static getFace() {
    return {
      friendly: 'Face',
      type: 'face',
      icon: 'face',
    };
  }

  static getPalm() {
    return {
      friendly: 'Palm',
      type: 'palm',
      icon: 'pan_tool',
    };
  }

  static getPin() {
    return {
      friendly: 'PIN',
      type: 'pin',
      icon: 'radio_button_checked',
    };
  }

  static getVoice() {
    return {
      friendly: 'Voice',
      type: 'voice',
      icon: 'keyboard_voice',
    };
  }

  static default () {
    return {
      friendly: '',
      type: '',
      icon: '',
    };
  }
}

(async () => {
  console.log('40 :', await BiometricMap.get(40));
  console.log('60 :', await BiometricMap.get(60));
  console.log('140 :', await BiometricMap.get(140));
  console.log('150 :', await BiometricMap.get(150));
})()

I would add that it seems a bit odd to load all the bioTypes every time, build up a data structure in bioTypes and then just invoke one of them, throwing all the others away. There seems to be a lot of work going on to achieve a single method call.

In its present form it could be reduced down to this:

static async get(bioType) {
  if (!bioType) {
    return BiometricMap.default();
  }

  const baseBioTypes = await BiometricService.fetchAll();

  for (const type of baseBioTypes.data) {
    if (type.bioType === bioType) {
      const captureType = type.captureType === 'passphrase' ? 'voice' : type.captureType
      const methodName = `get${captureType.charAt(0).toUpperCase()}${captureType.slice(1)}`
      return this[methodName]()
    }
  }

  // TODO: Handle fall-through
}

For the existing version to make sense there would need to be some way in which it retains the object bioTypes so that it gets used more than once. Even then I would be more inclined for it to contain just the mappings from the bioType to the methodName string rather than creating all those wrapper functions.

Leave a comment