Update the @Input field within an @Component following the completion of an Http Request in Angular2

I am currently developing an application using Angular2. One of the components has a button that, when clicked, triggers a post request to the server. The server will then respond with either an Ok(string) or a BadRequest(string).

I am facing difficulties in updating an @Input field of one of my components after receiving the response from the server.

Below is a simplified version of some of my classes.

My Component Class

@Component({
    moduleId: module.id,
    selector: 'model-comp',
    templateUrl: './model.component.html',
    styleUrls: ['./model.component.css']
})
export class MyComponent{
    @Input() model: Model;
    @Output() emitter: EventEmitter<Model> = new EventEmitter<Model>();

    public constructor(private service: MyService){}

    public toggle(): void {
        this.service.send(model.id, model.name){
            .subscribe(
                result  => this.onSuccess(result)),
                error   => this.onError(error),
                ()      => this.onComplete());
    }

    public onSuccess(result: string): void {
        if(result.inculdes("Some Text")) this.model.flag = true;
        else this.model.flag = false;
        this.emitter.emit(this.model);
    }

    public onError(error: any): void {
        //notification using bootstrap-notify
    }

    public onComplete(): void {
        //currently empty
    }
}

My Service Class

export class MyService{

    public send(id: string, name: string){
        return <Observable<string>>this.http
            .post('url', new Dto(id, name))
            .map(result => this.getData<string>(result))
            .catch(this.catchBadResponse);
    }

    private getData<E>(result: Response): E {
        //checking if result.status is ok
        var body = result.json ? res.json(): null;
        return  <E>(body || {});
    }

    private catchBadRespomse: (error: any) => Observable<any> = (error: any) => {
        var response = <Response>error;
        var json = response.json();
        var msg = json.Message;
        var errormsg = json?
            (json.error ? json.error: JSON.stringify(msg?msg:json)) :
            (response.statusText || 'Error?');
        return Obserable.of(errormsg);
    }

}

Template of MyComponent

<button (click)="toggle()"
[ngClass]="{'class1': true, 'class2': model.flag}">Text</button>

Template of Parent Component

<div *ngFor="let model of getList()">
    <model-comp [model]="model" (emitter)="onEmit($event)"></model-comp>
</div>

The onEmit Function

onEmit(evt: any): void{
    if(evt instanceof Model){
        var evtModel = evt as Model;
        this.list.find(search => search.id == evtModel.id)
            .isFav = evtModel.isFav;
    }
}

The issue I am encountering is that, despite sending and receiving data from the server, the property flag of my model does not update.

I suspect that the click event triggers a reload of the component, removing the observers of the EventEmitter.

Is there a way to prevent the reload, retain the observers of the EventEmitter, or another method to update the main object or the element class?

Answer №1

revision (refer to the comments below)

If the function getList() returns a new list each time it is called, the *ngFor directive will constantly re-render the items due to change detection repeatedly invoking getList().

Binding directly to a function that generates a new object or array on every call can lead to severe issues such as exceptions and a significant decline in performance.

It is generally advised against using method/function calls in the view. It is preferable to assign the list to a variable and bind to that variable instead of the function.

While it is acceptable to use ngOnInit() for initializing the list, any event handlers responsible for initialization or updates should also handle the list manipulation.

original text

If you modify the value of model passed from the parent component, the changes are reflected in the parent as well. Emitting the modified value as an event may be unnecessary.

It seems like you are altering the list (which is used in

<div *ngFor="let model of list">
) within the function onEmit(), triggering a rerender of the list by *ngFor.

Answer №2

It is not recommended to modify the @input property within the component itself. The component should be designed to respond to changes coming from its parent component instead.

Answer №3

CustomComponent.ts

export class CustomComponent{
    @Input() data: DataModel;
    //@Output() eventEmitter: EventEmitter<DataModel> = new EventEmitter<DataModel>();

    public constructor(private customService: CustomService){}

    public toggle(): void {
        this.customService.send(data.id, data.name){
            .subscribe(
                result  => this.handleSuccess(result)),
                error   => this.handleError(error),
                ()      => this.handleCompletion());
    }

    public handleSuccess(result: string): void {
        if(result.inculdes("Some Text")) this.data.flag = true;
        else this.data.flag = false;
        //this.eventEmitter.emit(this.data);
        this.customService.emitter.next(false);
    }

    public handleError(error: any): void {
        //display notification using bootstrap-notify
    }

    public handleCompletion(): void {
        //currently empty
    }
}

Custom Service

@Injectable // important
export class CustomService{
    public emitter: Subject<any> = new Subject();
    public send(id: string, name: string){
        return <Observable<string>>this.http
            .post('url', new Dto(id, name))
            .map(result => this.extractData<string>(result))
            .catch(this.handleBadResponse);
    }

    private extractData<E>(result: Response): E {
        //checking if result.status is ok
        var body = result.json ? res.json(): null;
        return  <E>(body || {});
    }

    private handleBadRespomse: (error: any) => Observable<any> = (error: any) => {
        var response = <Response>error;
        var json = response.json();
        var msg = json.Message;
        var errormsg = json?
            (json.error ? json.error: JSON.stringify(msg?msg:json)) :
            (response.statusText || 'Error?');
        return Obserable.of(errormsg);
    }

}

You can now subscribe to CustomService.emitter from anywhere in the application

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

Loading children routes using NgRx

In my Angular 9 app, I am facing a challenge where I need to dynamically load a specific module only when a certain property in my app state (ngrx) is not null. Currently, I have an AuthGuard set up in my routes using canActivate. My goal is to load the & ...

Error in Typescript cloud function: response object is not callable

My cloud function is structured like this: // const {onSchedule} = require("firebase-functions/v2/scheduler"); // The Cloud Functions for Firebase SDK to set up triggers and logging. import * as logger from "firebase-functions/logger&quo ...

Tips for configuring TypeScript in a monorepo to successfully compile private packages

I have configured a monorepo using turborepo that includes Nestjs for the backend and Nextjs for the frontend. To reuse prisma definitions, I separated them into their own package with its own tsconfig. In the index file of my database package where prism ...

Vue - the <script setup> element is unable to execute scripts

I have a functional widget from Atlassian that works as expected when used in an HTML file: <html lang="en"> <head> <script data-jsd-embedded data-key=<key> data-base-url=https://jsd-widget.atlassian.com src=https://jsd-w ...

Token generated by Sinch backend for use with Javascript and Android applications

I am currently exploring two distinct methods for creating the sinch authentication token on an app server. One approach is designed for the Android client while the other is tailored for the JS client. Is it feasible to utilize the same token for both the ...

Creating multiple maps in Angular 4 using Mapbox with a loop (ngFor)

Creating multiple maps using *ngFor requires the component div id to be ready before assigning it to mapbox's container variable. Otherwise, an error stating that the map id is not defined will occur if setTimeout is not used. The HTML for my compone ...

Update gulp configuration to integrate TypeScript into the build process

In the process of updating the build system for my Angular 1.5.8 application to support Typescript development, I encountered some challenges. After a complex experience with Grunt, I simplified the build process to only use Gulp and Browserify to generat ...

Angular: Dynamically add or delete an interceptor based on conditions

Is it possible to dynamically include or exclude an interceptor based on user selection? For my application, I want to enable Azure AD based SSO using the @azure/msal-angular package https://www.npmjs.com/package/@azure/msal-angular that provides an inter ...

transferring information to a PHP page using JavaScript without using AJAX requests or form submissions

I am currently working on a PHP page where I receive POST data using some functions, without relying on AJAX for page refresh. At the moment, I have a form that includes hidden fields holding dynamic data, which is then sent using JS like this: document.m ...

Issues with click events in the navigation menu

How can I make my menu close when clicking on other parts of my website, instead of opening? I know that I should use a click event for this, but when I implemented a click event, my menu encountered 2 unwanted problems: 1- Whenever I clicked on a menu i ...

Combining a plain object with a TypeScript class

I am currently working on developing a function that dynamically defines a class extending another class based on a passed object. Here is an example of what I am aiming to achieve: const ObjectMixin = function<T>(obj: T): new () => T { return ...

Shifting hues of colors on website based on geographical location

Working on a new project, I encountered an issue with two elements sharing the same class but displaying different color shades. Has anyone experienced this before and knows what could be causing it? UPDATE Here is an excerpt of the code: The Class: .su ...

What is the process of invoking a secondary "external" function with Nodejs, Expressjs, and bluebird?

Struggling with creating a nodejs application, a new area for me. I've managed to work with Promises and fetch data from a database. Take a look at the code below: myModel.js var express = require('express'); var app = express(); var Promi ...

Implementing a delay between two div elements using jQuery

I have two Divs with the class "sliced", each containing three images with the class "tile". When I animate using jQuery, I am unable to add a delay between the "sliced" classes. However, I have successfully added a delay between the "tile" classes. index ...

What steps are needed to generate a production version of a TypeScript monorepo application that can be deployed to an Azure Function App?

I've been grappling with understanding Typescript project references and their intended use in a production build, especially for an Azure Function App. I'm not utilizing any monorepo functionality at the package manager level, such as npm worksp ...

Is there a method in VBA to access elements generated by javascript code?

After spending several hours conducting thorough research on Google (including browsing StackOverflow), I've been trying to find a method that would allow me to target HTML elements generated by JavaScript in VBA. For instance, using ie.Document.getE ...

Sort the array of objects based on the nested attribute

I am facing a challenge in ordering an array based on a nested object. The array contains information about objects on a timeline and I would like to sort it by the start position defined within nested arrays. Currently, I am able to iterate through the ar ...

Setting attributes on dynamically appended elements using Jquery

Whenever a user clicks on another user's name, a popup will appear with a form to send a message to that specific user. The goal is to dynamically change the 'action' attribute to include the user's ID in the form submission URL. Althou ...

typescript decorator implemented on a class that is nested within another class

Is it possible to decorate a nested property within a class? Let's explore with an example: function log(name: string = 'DecoratedProp') { return function logHandler(target: any, field: any) { // get the key ...

How come I am getting the desired section from my split string only after running the function twice?

I've been working on a function that retrieves data from a form and sends it to a POST request, which in turn sends it to my MongoDB database. The following code snippet showcases the process of uploading an image to IPFS, extracting the IPFS hash fro ...