Home > front end >  Typescript: Reactive forms patchValue not working with private properties and getters/setters
Typescript: Reactive forms patchValue not working with private properties and getters/setters

Time:01-01

I'm using an Angular/.net application with .net6.0 framework, with Angular: 13.1.2, Node: 16.13.1 and EMCAScript 5.

I have the following person class in typescript.

export class Person {

  private _name: string = '';
    
  constructor() {
  }

  public get name(): string {
    return this._name;
  }

  public set name(value: string) {
    this._name = value;
  }
}

While compiling the following person.model.js file is generated. But I'm under the impression the Object.defineProperties doesn't work.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Person = /** @class */ (function () {
    function Person() {
        this._name = '';
    }
    Object.defineProperty(Person.prototype, "name", {
        get: function () {
            return this._name;
        },
        set: function (value) {
            this._name = value;
        },
        enumerable: true,
        configurable: true
    });
    return Person;
}());
exports.Person = Person;
//# sourceMappingURL=person.model.js.map

Whenever I try to update my Reactive form using an object no data is updated. While I'm debugging I only notice the '_name' property in the 'Object.Keys' array.

 this.personForm.get('person')?.patchValue(person);

Reactive form is defined the following way.

    return formBuilder.group({
      person: formBuilder.group({
        name: [
          '',
          [
            Validators.required
          ]
        ]
      })
    });

Whenever I update my class with 'Object.DefineProperty' the above code updating the Reactive form seems to work without problems. Also the 'Name' property seems to have been added to the 'Object.Keys' array.

export class Person {

  private _name: string = '';
    
  constructor() {
    Object.defineProperty(this, 'name', {
      set: function (value: string) {
        this._name = value;
      },
      get: function () {
        return this._name;
      },
      enumerable: true
    })
  }
}

Do I need to add the Object.defineProperties in the .ts file or is my application not using the generated .js files ?

EDIT:

Component

export class PersonComponent implements OnInit {

  personForm!: FormGroup;

  constructor(
    private _fb: FormBuilder) {}

  private CreateFormGroup(formBuilder: FormBuilder): FormGroup {
    return formBuilder.group({
      person: formBuilder.group({
        name: [
          '',
          [
            Validators.required
          ]
        ]
      })
    });
  }

  ngOnInit(): void {
    this.personForm = this.CreateFormGroup(this._fb);
  }

  setPerson(person: Person) {
    if (person != undefined) {
      this.personForm.get('person')?.patchValue(person);
    }
  }

}

HTML:

<mat-card >
  <mat-card-title>Person</mat-card-title>
  <mat-card-content>
    <form [formGroup]="personForm">
      <div formGroupName="person">
        <mat-form-field >
          <input id="name" matInput aria-label="name" type="text" formControlName="name" autofocus required />
          <mat-placeholder >name</mat-placeholder>
        </mat-form-field>
      </div>
      <br />
    </form>
  </mat-card-content>
</mat-card>
<app-person-selection (personEvent)="setPerson($event)"></app-person-selection>

CodePudding user response:

TypeScript does transpiles the getter/setter into an equivalent Object.defineProperty syntax.

But if you carefully look at the transpiled code, you will observe that the name property is defined on Person.prototype i.e on function's prototype, and not on the function itself.

// Transpiled code with v3.9.7
var Person = /** @class */ (function () {
    function Person() {
        this._name = '';
    }
    Object.defineProperty(Person.prototype, "name", {  // <----
        get: function () {
            return this._name;
        },
        set: function (value) {
            this._name = value;
        },
        enumerable: false,
        configurable: true
    });
    return Person;
}());

Whereas in the 2nd scenario, wherein you manually define Object.defineProperty, you are passing 1st argument as this, and not Person.prototype.

// Transpiled code with v3.9.7
var Person = /** @class */ (function () {
    function Person() {
        this._name = '';
        Object.defineProperty(this, 'name', {  // <----
            set: function (value) {
                this._name = value;
            },
            get: function () {
                return this._name;
            },
            enumerable: true
        });
    }
    return Person;
}());

This is the reason why name property is not visible within Object.keys in 1st scenario (getter/setter), but is visible in the 2nd scenario. And for the same reason the patchValue doesn't work as name is not an object's own enumerable property.

  • Related