2

The issue in a single line: Is it possible to pass a property as a parameter to a functon and update it in the function itself?

Scenario

I have a profile class in my ionic project which opens several modals that are bound to the properties in it (name, gender, email, etc). All these methods are same, the only thing that makes them different is where to set the response after the modal is closed.

One example:

async changeName() {
    const modal = await this.modalCtrl.create({
      component: ChangeNameModalPage,
      cssClass: 'default-modal',
      componentProps: {
        'content': this.name // get class property value
      }
    });

    modal.onDidDismiss().then((modalDataResponse) => {
      if (modalDataResponse.data !== null && modalDataResponse.data !== undefined) {
        this.name = modalDataResponse.data; // set value back to class property
      }
    });

    return await modal.present();
  }

I have 6 functions that are the same as this, the only difference is the property that is consumed/set in the function.

Profile view


The problem

This view will, probably, grow up in a few months from now receiving other properties and they will do the exact same thing, where the property will be added, which will cause too much code duplication.

My idea is to make it OO so the maintenance would be easier. I'm still learning typescript and I don't know if it has something similar to java reflection, where this kind of update would be a piece of cake.

My dream world would be this (roughly speaking):

  async changeField(classProperty, modalPage) {
    const modal = await this.modalCtrl.create({
      component: modalPage, // the component that will be used
      cssClass: 'default-modal',
      componentProps: {
        'content': classProperty // get current property value
      }
    });

    modal.onDidDismiss().then((modalDataResponse) => {
      if (modalDataResponse.data !== null && modalDataResponse.data !== undefined) {
        classProperty = modalDataResponse.data; // set back the property value
      }
    });

    return await modal.present();
  }

  async changeName() {
    await this.changeField(this.name, ChangeNameModalPage)
  }

  async changePhone() {
    await this.changeField(this.phone, ChangePhoneModalPage)
  }

  async changeEmail() {
    await this.changeField(this.email, ChangeEmailModalPage)
  }

What have I found so far

Throughout several posts in stackoverflow I came to know about the generic in keyof operator, but I am able to get the value but not set it back to the property that comes as a parameter.

I've checked the typescript documentation and other posts to know about it, but I can't find something that specifically sets the value back to the property.

The closest that I've found was this post here where the user is able to increment the external variable when it is a number. I've tried doing so with string but when I do so it states that my type cannot be used as an indes type when trying to set the value back to it, although when sending it, it works just fine (I changed the property to any in the example below):

Error

Is this type of interaction in the code that I'm trying to achieve possible with typescript? Or am I doing something wrong that I'm not being able to spot?

Solution

As taught by @Cerbrus in the answer below, here's the (much more tidy, beautiful and maintainable) code.

  async changeName() {
    await this.changeField('name', ChangeNameModalPage);
  }

  async changeBirthday() {
    await this.changeField('birthday', ChangeBirthdayModalPage);
  }

  async changeCPF() {
    await this.changeField('cpf', ChangeCPFModalPage);
  }

  async changePhone() {
    await this.changeField('phone', ChangePhoneModalPage);
  }

  async changeEmail() {
    await this.changeField('email', ChangeEmailModalPage);
  }

  async changeGender() {
    await this.changeField('gender', ChangeGenderModalPage);
  }

  async changeField(classProperty: keyof this, modalPage) {
    const modal = await this.modalCtrl.create({
      component: modalPage,
      cssClass: 'default-modal',
      componentProps: {
        'content': this[classProperty]
      }
    });

    modal.onDidDismiss().then((modalDataResponse) => {
      if (modalDataResponse.data !== null && modalDataResponse.data !== undefined) {
        this[classProperty] = modalDataResponse.data;
      }
    });

    return await modal.present();
  }
Banns
  • 576
  • 4
  • 12

1 Answers1

3

You were close.

Looking at your original code, you're trying to set a property on this, in the callback.

You'll just need to specify that classProperty is a key of this:

async changeField(classProperty: keyof this, modalPage) {
    const modal = await this.modalCtrl.create({
    component: modalPage, // the component that will be used
    cssClass: 'default-modal',
    componentProps: {
        'content': this[classProperty] // get current property value
    }
    });

    modal.onDidDismiss().then((modalDataResponse) => {
        if (modalDataResponse.data !== null && modalDataResponse.data !== undefined) {
            this[classProperty] = modalDataResponse.data; // set back the property value
        }
    });

    return await modal.present();
}

You then call that, using the key you want to update:

await this.changeField('name', ChangeNameModalPage)

(Your IDE should be giving you suggestions on what that parameter can be)

In case the this scope changed between the changeField function and the onDidDismiss callback, you can use keyof MyObjectType instead of keyof this to use the correct type.

Banns
  • 576
  • 4
  • 12
Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • 1
    Indeed, that did the trick! Thank you cerbrus. The only thing that had to be modified in the code was that in the modal opening componentProps I had to also add this[classProperty]. The way it is in your example would always lead to **name** value. I've modified it in your answer – Banns Oct 27 '21 at 13:36
  • sorry to drag this a bit longer, but is it also possible to set the value to a property inside of an object. For example, I have the object User which has all these fields and I want to update the name inside of the object user. I tried setting this.user[classProperty] to check if it would render the way I hoped so, but was not the case. – Banns Oct 28 '21 at 11:46
  • @Banns: the syntax you have there is valid, other than that, there's not enough information to help you... Maybe that's a subject for a new question? – Cerbrus Oct 28 '21 at 12:28