The ngOnInit child function fails to activate when trying to assign a fresh object

On the parent page, I am passing user data to a child component in this way:

<ng-container *ngIf="leaderboard">
    <app-leaderboard-preview [user]="user" (click)="goToLeaderboard()"></app-leaderboard-preview>
</ng-container>

In the parent page's ngOnInit(), I subscribe to an observable that returns a user object and sets the user variable after parsing the result as shown below:

combineLatest([this.userQuery$]).subscribe((results) => {
  Promise.all([this.parseUser(results[4])])
})

I have learned that ngOnChanges() only triggers when there is a new object created. Therefore, I use Object.assign() to assign the new user object as a separate entity for the app-leaderboard-preview component.

  parseUser(user) {
    return new Promise((resolve) => {
      if(user) {
        this.user = Object.assign({}, user);
        resolve(user);
      } else {
        resolve(user);
      }
    })
  }

The component loads successfully, but the user's ranking can change. When a user pulls down to refresh the page, the value should update. However, it does not. The code snippet below attempts to refresh the page without a complete reload.

  doRefresh(event) {
    if (this.user) {
      this.userQuery$ = this.db.query$(`user/find?id=${this.user.id}`);
      combineLatest([this.userQuery$]).subscribe((results) => {
        Promise.all([this.parseUser(results[4])])
      })
  }

This method calls the parseUser function to update the user object used by the app-leaderboard-preview component.

Despite passing a "new" object to the component, ngOnChanges is not triggered. What could be missing in my approach?

Answer №1

Update

After a series of revisions, Jordan identified that an async pipe was assigning to the user variable, thus overriding his modifications to this.user.

*ngIf = user$ | async as user

Initial Response

To resolve this issue, using Object.assign({}, object) should do the trick. This has been tested on Angular 7.

this.user = Object.assign({}, user);

Alternatively, if you utilize lodash, you can implement the following:

this.user = _.clone(user);
this.user = _.cloneDeep(user);

For more information on lodash's clone and cloneDeep functions:

https://lodash.com/docs/4.17.11#clone

https://lodash.com/docs/4.17.11#cloneDeep

If you'd like to see a demonstration, check out the Stackblitz demo below:

https://stackblitz.com/edit/ngonchangeswithobject

Answer №2

I encountered a similar issue in the past and I'm wondering if there is a solution available.

To work around this problem, I used get() and set() methods on my input variable like so. However, this approach may not be ideal, especially when dealing with multiple input variables.

private _userData: any;
@Input()
set userData(value: any) {
this._userData = value;
// perform necessary ngOnChanges operations here
this.ngOnChanges();
}
get userData(): any {
return this._userData;
}

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

EventEmitter is failing to refresh the properties bound with [(ngModel)]

I am currently working with the forms module and Google Maps to update the geocode based on the search box provided on the map. The map is a separate component that emits the Geocode using the function geoCodeChange(). This event is handled by another func ...

"ENUM value is stored in the event target value's handle property

My issue lies with the event.target.value that returns a 'String' value, and I want it to be recognized as an ENUM type. Here is my current code: export enum Permissions { OnlyMe, Everyone, SelectedPerson } ... <FormControl> & ...

What is the reason for the Enter key being assigned to the incorrect button?

In my Angular 13 form, there are several buttons. One of them is used to add a new row to the form: <div class="col-md-2 offset-md-8"> <button class="badge rounded-pill bg-secondary mt-2" (click)="addRow()& ...

Which symbol or character corresponds to the public "get symbol" method?

While going through some Typescript code, I came across a line that is giving me trouble to understand. const symbol = Symbol.for('symbolname'); class Something { public get [symbol]() { return true; } ... } I am confused abou ...

Tips for properly formatting a fixed table header

I'm currently facing an issue with the sticky header style in my data table. I have created a simple Angular component along with a specific directive: sticky.directive.ts @Directive({ selector: '[sticky]' }) export class StickyDirecti ...

Retrieve the unchanged data from the source before any modifications using ag-Grid

Is the original data saved anywhere when accessing data with the ag-Grid API? For instance, can it be retrieved from: api.getRowNode(id) This information could come in handy, especially if you want to highlight an edited cell by comparing values and chan ...

Accessing attributes of a parent class object from within a child object

Imagine having four tabs within an Angular component, each with its own set of criteria for being displayed. Here's a high-level overview of the scenario. export class DisplayTabs { foo: true; bar: false; tabs: { 'A': { order: 1, g ...

What is the process of integrating an MVC View into the template URI of a typescript component?

I am currently working on a project using MVC core and AngularJS2 with TypeScript. I recently added a new component (View located in Views\Home\Default.cshml) but I am encountering some issues. Below is the code I have tried: import { Componen ...

Leverage the power of Template Input Variables beyond NgFor loop limitations

An example that is often encountered: <div *ngFor="let hero of heroes$ |async"> <span>{{hero.name}}</span> </div> I am wondering how to achieve the same functionality when heroes is not an Observable of an array of Heros, but in ...

Using const enums across multiple files with react-scripts-ts

Within our project, we have two distinct subprojects (backend and frontend) that are compiled independently. The frontend utilizes react-scripts-ts, so it is crucial to avoid cross-imports between the two subprojects to maintain the integrity of the transp ...

Exchange information between two components and a service in a continuous loop

My Angular application retrieves a JSON object from an API, and I want to accomplish this through a service. The app has a search component that queries the service, which in turn fetches the data. View example diagram Next, the second component needs t ...

Reset the angular child component trigger by its parent component, which is a modal popup

I'm facing an issue with my Angular application where I have a child component within a parent modal window implemented using ngx-smart-modal. When I close the modal using the method: this.ngxSmartModalService.getModal('ModalNameComponent') ...

Issue with Angular 8 Animations when loading a module lazily - Encountered Synthetic Property Error

I currently have 2 different modules in my project, namely the app.module and a lazy.module. The lazy.module is specifically designed to be lazy loaded into the main app.module through routing mechanisms. const routes: Routes = [ { path: '', lo ...

Utilizing Pipes within a Method in Angular 2 along with Dependency Injection triggers an "Insufficient Number of Arguments" error

I am searching for a solution to incorporate a custom pipe into my class. The custom pipe itself ( referenced from this source, many thanks ) involves injecting a dependency (the DomSanitizationService). import { Pipe, Inject, Injectable } from '@ang ...

Using the decorator in VueJS Typescript allows you to easily define dynamic computed properties

On my hands is a component structured like this: @Component({ computed: { [this.stateModel]: { get() { return this.$store[this.stateModel]; } } } }) class Component extends Vue{ @Prop({ de ...

New feature in Next.js 13: Utilizing a string within className

Looking for a way to dynamically generate radio buttons in Next.js using a list of strings? Utilizing Tailwind CSS classes, you can modify the appearance of these buttons when checked by leveraging the peer/identifier classname. But how do you include th ...

Sharing data between different Angular components that are not directly related can be achieved by utilizing a service in Angular

This is the service class for managing data export class DataService { public confirmationStatus = new Subject(); updateConfirmationStatus(status: boolean) { this.confirmationStatus.next(status); } getConfirmationStatus(): Observable<any&g ...

Exploring the MVVM architecture in React and the common warning about a missing dependency in the useEffect hook

I'm currently in the process of developing a React application using a View/ViewModel architecture. In this setup, the viewModel takes on the responsibility of fetching data and providing data along with getter functions to the View. export default f ...

Are your custom guards failing to function properly now?

Previously, the code below was functioning properly until typescript 2.0: class Aluno{ escola: string; constructor(public nome: string){ this.escola = ""; } } class Pessoa{ morada: string; constructor(public nome: string){ this.morada = ...

How can I show distinct values in the Angular Material dropdown menu?

I am currently working on implementing a feature where I need to show unique options for the select using angular material. Below is what I have so far: where appitem is an array of items. <mat-form-field> <mat-select placeholder="Select app ...