1

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
  • If the detail component is data bound (with an @Input property) and the bound data changes it will refresh itself. Though this is not the case please post the essential code (html + ts) so we can help. Commented Sep 6, 2022 at 9:10
  • What is the components ChangeDetectionStrategy? Default? Or OnPush? Then you should invoke changeDetectorRef's (injected in components constructor) detectChanges to have changes reflected in the component's view (html) I think. Although for @Input properties I doubt if they don't update whichever ChangeDetectionStrategy is applied. Commented Sep 6, 2022 at 9:12

2 Answers 2

2

you can use @Input() setters

Here is an example that shows what I mean

stackblitz

I hope it is useful for you

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. This definitely solves the problem. It feels a bit like there should be a better solution for this? Let's see what other solutions come by.
-1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.