Issue with Patching values in Reactive forms when working with private properties and getters/setters in Typescript

My current tech stack includes an Angular application with the .NET 6.0 framework, running on Angular version 13.1.2, Node version 16.13.1, and EMCAScript 5.

I have defined a Person class in TypeScript as follows:

export class Person {

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

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

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

When I compile my code, it generates a person.model.js file where I suspect Object.defineProperties is not functioning as expected.

"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

While trying to update my Reactive form using an object, I notice that no data gets updated. Upon debugging, I observe that only the '_name' property appears in the 'Object.Keys' array.

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

The Reactive form setup looks like this:

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

However, when I utilize 'Object.DefineProperty' in my class, the aforementioned issue seems to be resolved, and the 'Name' property correctly shows up in 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
    })
  }
}

Should I include Object.defineProperties in the .ts file, or could it be that my application is not utilizing the generated .js files properly?

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

Answer №1

When TypeScript transpiles getter/setter methods, it converts them into the equivalent syntax of Object.defineProperty.

Upon closer inspection of the transpiled code, you'll notice that the name property is actually defined on Person.prototype, which is the prototype of the function, rather than on the function itself.

// Transpiled code using version 3.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;
}());

In contrast, in the second scenario where you manually define Object.defineProperty, you're passing the first argument as this instead of Person.prototype.

// Transpiled code using version 3.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 difference is why the name property is not visible within Object.keys in the first scenario with getters and setters, but is visible in the second scenario. This also explains why the patchValue method does not work properly, as name is not considered an object's own enumerable property.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Dealing with conflicts between RMQ and TypeORM in a NestJS

Every time I try to use TypeOrm, RMQ crashes. I can't figure out why. Utilizing the library golevelup/nestjs-rabbitmq has been a struggle for me. I've spent 7 hours trying to resolve this issue. @Module({ imports: [ ConfigModule.f ...

Angular 6 - Unlocking the Secrets of Filtered Results

I need help accessing the filteredArray in my .ts component. Currently, it is only accessible within the ng-container. <ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray"> <tr *ngFor="let user of filteredArray ...

Explain what one-time typescript interfaces are

As someone who has been using React for quite some time, I am now looking to integrate Typescript into my projects. In the past, I would create large "container" objects like this: const theme = { colors: { primary: '#00f', accent: &ap ...

What is the syntax for adjusting background-position with ngStyle in Angular 4?

It's finally Friday! I'm a bit confused about how to properly set the background-position-x property in an [ngStyle] directive in Angular 4 with Ionic 3. Can someone guide me on the correct way to implement background-position-x? I expect the ou ...

I have enabled sourceMap globally in my project, yet TypeScript sources are not visible in my Firefox browser debugger when I am working with Angular 14

When launching an Angular application in dev mode using ng serve, source maps are supposed to load instantly on web browsers. If "sourceMap": true is configured, as I've gathered from my online research. Most developers don't seem to h ...

What causes the undefined value of the private member in Angular 2 service?

I am struggling to understand why, after setting a private member of a service, it ends up being undefined when I revisit that service. getResult (): Observable<result[]> { return this.http.get(this.url) .map(this.extractData) .catch ...

Exploring the angular2-meteor Component Testing

For my web app built with angular-meteor, I attempted to write some test functions in order to test the functionality. However, when using the command meteor test --driver-package practicalmeteor:mocha, it does not execute any test files and results in 0 f ...

Customize Typing for Properties in Subclasses of Lit-Element Using TypeScript

When extending a lit-element Class to add more specific typing, should the @property decorator be overridden or just the type and initializer? For example, consider the following code: interface AB { a: number, b: string, } @customElement('my- ...

Obtain a document from an Angular 2+ software program

In the process of developing a web application using Angular 9, there is a requirement from the client that a PDF should be downloaded upon submitting a form on a particular page. The backend (server-side) is able to generate the PDF file successfully and ...

Do you need to have typing or definition files for each JavaScript library you utilize in TypeScript?

Do you need typings for every JavaScript library used in TypeScript? If not, how can you eliminate errors and utilize a library that doesn't have available definition files? import { NotificationContainer, NotificationManager } from 'react-notif ...

What is the recommended substitute for the "any" type in TypeScript?

Code Slider.tsx import { useSelector, connect } from "react-redux"; import { ProductType, ProductItem, StateType } from "types"; const Slider = ({ products, number }: any) => { ------------------> what type? // const number = ...

How to package dependencies in an Angular 7 library without directly including them in the main application

In my Angular 7 project, I utilized the @angular/cli to set it up and then added a custom library using ng generate library. The application functions smoothly in dev mode without any issues. I made sure to isolate the dependencies relevant to the library ...

Modify the [src] attribute of an image dynamically

I have a component that contains a list of records. export class HomeComponent implements OnInit { public wonders: WonderModel[] = []; constructor(private ms: ModelService){ ms.wonderService.getWonders(); this.wonders = ms.wonder ...

Confused about how to correctly utilize template variables within an *ngIf condition

My goal is to create a feature where users can adjust the number of rows in the catalog view on spiritwoodart.com. However, I encountered an issue at the initial stage of implementation and believe it stems from a basic misunderstanding. I am struggling to ...

Transform nested properties of an object into a new data type

I created a versatile function that recursively converts nested property values into numbers: type CastToNumber<T> = T extends string ? number : { [K in keyof T]: CastToNumber<T[K]> }; type StringMap = { [key: string]: any }; const castOb ...

Leveraging Angular for Parsing JSON Data

I have a JSON object that I want to use in my code. The goal is to assign the values of certain properties from the JSON object to four variables in my Angular project. These variables are named as follows: authorText : string; titleText : string; duratio ...

Upgrade the map function from rxjs5 to rxjs6

I need help transitioning this code from rxjs5 to rxjs6: return this.http.put(url, image, options).map((res, err) => { return res; }).catch(err => { if (err.error instanceof Error) { return err.error; } else { t ...

Verify an entry with exactly 7 numerical digits

When inputting data, the user is limited to entering only 7 figures. If more than 7 figures are entered, an error message will display: "You need 7 digits". For instance, if the user enters something like this: 12345678910 The error message is correctly ...

I need assistance in deactivating all functions activated by @typescript-eslint/ban-types

I am constantly battling with the "@typescript-eslint/ban-types" rule. It plagues me with countless errors in the hundreds on my large project, making it a nightmare to manage and resolve. Despite having the following configuration in place, eslint seems ...

Iterate over an array of objects containing identical data on certain objects and display it only once

I am working with an array of product objects that look like this products: [ { id: 1, drinkName: "Chivita", category: "Juice", description: "The best drink ever" }, { id: 1, drinkName: " ...