Force update of an Angular component, without changing @Input attributes

Viewed 41

I'm picking up Angular (and Typescript with it). I created a small app that has two components. It's for keeping track of work hours (yeah, I know, there are COTS products for that, this is for educational purposes).

One component is for creating a new hour entry (employee + project + hours + date). It has some input boxes and an "Add" button.

The other component shows the hours that the user booked for the selected day. When the "Add" button is clicked in the first component, this component should refresh the data from the API/database.

When I google this, all answers point to @Input arguments. In this case, the detail component has an employeeId and a date as input. These inputs don't change. Only the result (database content) changes for those parameters. I thought of calling one of the methods on the detail component from the container of those two components, but I found no way to do that.

How can I force the detail component to refresh, after the "Add" button is clicked?

Would be grateful for any pointers.

EDIT: Relevant code

dailyHoursOverview.components.ts

import { HttpClient } from "@angular/common/http";
import { Component, Input, OnChanges, OnInit, SimpleChanges } from "@angular/core";
import { Activity } from "../shared/Activity";
import { Employee } from "../shared/Employee";
import { Project } from "../shared/Project";

@Component({
    selector: "hour-daily",
    templateUrl: "./dailyHoursOverview.component.html"
})
export class DailyHoursOverview implements OnInit, OnChanges
{
 //   @Input() date: string = "";
    @Input() employeeId: number = 0;
    @Input() date: string = "";
    _http: HttpClient;

    entries: HourEntry[] = [];
    totalHours: number= 0;

    constructor(http: HttpClient) {
        this._http = http;
    }

    ngOnChanges(changes: SimpleChanges): void {
        this.refreshData();
    }

    ngOnInit(): void {
        this.refreshData();
    }

    refreshData() {
        this._http.get<HourEntry[]>("http://localhost:5257/Employee/" + this.employeeId + "/HourEntry/ByDay/" + this.date)
            .subscribe(
                result => { 
                    this.entries = result;
                    this.totalHours = this.entries.reduce((sum, entry) => sum + (entry.hours), 0); 
                },
                error => console.error(error)
            );
    }
}

interface HourEntry {
    project: Project;
    activity: Activity;
    employee: Employee;
    hours: number;
    date: string;
}

main.component.html (container of both sub components)

<div class="container">
    <div class="row">
        <div class="col-md-6 col-sm-8">
            <hours-new [employeeId]="employeeId" [hourDate]="date" (entryCreated)="onEntryCreated();"></hours-new>
        </div>
    </div>
    <div class="row">
        <div class="col-12">
            <hour-daily [employeeId]="employeeId" [date]="date"></hour-daily>
        </div>
    </div>
</div>

newHourEntry.component.ts

import { HttpClient } from '@angular/common/http';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Activity } from '../shared/Activity';
import { Project } from '../shared/Project';
import { Employee } from '../shared/Employee';
import { NewHourEntryDto } from '../shared/NewHoursEntryDto';

@Component({
    selector: "hours-new",
    templateUrl: "./newHourEntry.component.html"
})
export class NewHourEntryComponent implements OnInit {
    @Input() employeeId: number = 0;
    @Input() hourDate: string = "";
    @Output() entryCreated: EventEmitter<any> = new EventEmitter();

    private _http: HttpClient;

    employeeName: string = "";
    hourEntry: NewHourEntryDto;
    projects: Project[];
    activities: Activity[];

    constructor(http: HttpClient) {
        this._http = http;
        this.hourEntry = { "activityId": 0, "projectId": 0, "date": "", "hours": 0, "employeeId": 0 };
        this.projects = [];
        this.activities = [];
    }
    
    ngOnInit(): void {
        // Get options for comboboxes
        this._http.get<Project[]>('http://localhost:5257/Project')
            .subscribe({
                next: result => { this.projects = result; },
                error: error => console.error(error)
            });

        this._http.get<Activity[]>("http://localhost:5257/Activity")
            .subscribe({
                next: result => { this.activities = result; },
                error: error => console.error(error)
            });

        this._http.get<Employee>("http://localhost:5257/Employee/" + this.employeeId)
            .subscribe({
                next: result => { this.employeeName = result.name; },
                error: error => console.error(error)
            });
    }

    createHourEntry(): void {  // OnClick for the "Add" Button
        this.hourEntry.date = this.hourDate;
        this.hourEntry.employeeId = this.employeeId;
        this._http.post("http://localhost:5257/HourEntry", this.hourEntry)
            .subscribe({
                next: result => { }, 
                error: error => console.error(error)
            });
        this.entryCreated.emit();
    }
}
2 Answers

you can use @Input() setters

Here is an example that shows what I mean

stackblitz

I hope it is useful for you

I would let main.component.html handle the creation and refreshing the data (smart) and only pass data to newHourEntry and dailyHoursOverview (dumb) cfr Smart and dumb components

Related