Objects That Are Interconnected in Typescript

I have been delving into TS and Angular. I initially attempted to update my parent component array with an EventEmitter call. However, I later realized that this was unnecessary because my parent array is linked to my component variables (or so I believe, hence why I am seeking clarification).

I dynamically create my child components and initialize the child objects from the parent array. Is my template-task task$ Object merely a reference to the $task object in my parent array?

Parent component:

<div *ngIf="task" class="elements">
    <app-template-task (ping)="update($event)" [task$]="task" *ngFor="let task of $tasks"></app-template-task>
</div>

Child component HTML:

        <div class="checkbox">
        <img *ngIf="task$.done" (click)="changeStatus($event)" src="../../../assets/checked.png" alt="">
        <img *ngIf="!task$.done" (click)="changeStatus($event)" src="../../../assets/unchecked.png" alt="">
    </div>
    <div class="text">
        <input type="text" [(ngModel)]="task$.todoText" (blur)="changeText($event)" placeholder="Name des Todo punkts">
    </div>

TS from parent:

  public task: boolean;
  public taskDone: boolean;
  public $tasks: Todo[];
  public $tasksdone: Todo[];

  constructor() {
    this.task = true;
    this.taskDone = true;
    this.$tasks = [
      {
        id: 0,
        todoText: "Task 1",
        done: false,
        position: 1
      },
      {
        id: 1,
        todoText: "Task 2",
        done: false,
        position: 2
      }
    ]
    this.$tasksdone = []
  }

TS from Child:

@Input() task$!: Todo; //! erlaubt es das theoretisch task auch null sein darf
@Output() ping: EventEmitter<any> = new EventEmitter<any>();

constructor() {
}

  public changeStatus(event? : any): void{
    this.task$.done = !this.task$.done
    this.sendEvent("changed")
  } 

  public changeText(event? : any): void{
    console.log(this.task$.todoText)
    this.sendEvent("textChanged")
  }

  private sendEvent(eventIdentifier: string): void{
    const eventObject: Eventping = {
      label: eventIdentifier,
      object: this.task$
    }
    this.ping.emit(eventObject)
  }

Answer №1

A reference is being passed with [task$]="task"

Having references in data bindings can be beneficial. However, changing objects in child components is not recommended. It's best to only access your data from an @Input and avoid altering it.

The parent component (where the data originates) should be the only place for data updates.

I suggest making your child component a "dumb" one, meaning it simply displays data and communicates changes to the parent, which then handles the data accordingly.

child.component.html :

<div class="checkbox">
    <img *ngIf="task.done" (click)="changeStatus()" src="../../../assets/checked.png" alt="">
    <img *ngIf="!task.done" (click)="changeStatus()" src="../../../assets/unchecked.png" alt="">
</div>
<div class="text">
    <input type="text" [value]="task.todoText" (change)="changeText($event)" placeholder="Enter Todo item name">
</div>

child.component.ts :

@Input() task: Todo;
// Refer to Partial documentation here: https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype
@Output() ping: EventEmitter<any> = new EventEmitter<Partial<Todo>>();

constructor(){}

public changeStatus(): void {
  this.ping.next({ done: !this.task$.done });
} 

public changeText(newText: string): void {
  this.ping.next({ todoText: newText });
}

The parent component should manage updates and modify the data accordingly :)

parent.component.html :

<div *ngIf="task" class="elements">
   <app-template-task (ping)="update(idx, $event)" [task]="task" *ngFor="let task of $tasks; let idx = index"></app-template-task>
</div>

parent.component.ts :

public task = true;
public taskDone = true;
public tasks: Todo[] = [
  {
    id: 0,
    todoText: "Task 1",
    done: false,
    position: 1
  },
  {
    id: 1,
    todoText: "Task 2",
    done: false,
    position: 2
  }
];
public tasksDone: Todo[] = [];

constructor(){
}

update(taskIdx: number, changes: Partial<Todo>) {
  // Updating the reference by creating a new array using the .map method
  // This is necessary for Angular Change Detection to work properly.
  this.tasks = this.tasks.map((task, idx) => {
    if (idx === taskIdx) return { ...task, ...changes };
    return task;
  });
  // Additional operations can be performed as needed
}

That covers the main points. I hope this information proves helpful :)

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

Injecting a controller into an AngularJS module within an anonymous function

As a newcomer to the world of javascript and just beginning to work with angular.js, I have a question. I'm wondering if there is a method for injecting a controller into a module that is declared within an anonymous function. This is how my code cu ...

Angular: Converting JSON responses from HttpClient requests into class instances

I am facing an issue with the following code: public fetchResults(searchTerm: string): Observable<Array<SearchResult>> { let params = new HttpParams().set('searchTerm', searchTerm); return this.http .get<Array< ...

Exploring the possibility of detecting page scrolling in Javascript by clicking on scroll bars

I have implemented a unique feature on my page that allows users to scroll up and down using custom buttons I created. This functionality is achieved by smoothly transitioning between anchor points on the page using jQuery's animate function. However ...

Retrieve the content following a successful loading of the remote URL

I have been utilizing this function to retrieve content from a Remote URL function fetchContent($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $scrapedPage = curl_exec($ch); curl_close($ch); $content = $scrapedPage; return ...

TypeScript is still throwing an error even after verifying with the hasOwnProperty

There exists a type similar to the following: export type PathType = | LivingstoneSouthernWhiteFacedOwl | ArakGroundhog | HubsCampaigns | HubsCampaignsItemID | HubsAlgos | HubsAlgosItemID | TartuGecko | HammerfestPonies | TrapaniSnowLeop ...

I'm seeking an easy method to adjust the x and y coordinates of a popup rectangle with a fixed size. Can anyone provide

Is there a way to create a simple pop-up rectangle, possibly using jQuery or a similar tool, that presents a scaled-down canvas view of a larger browser window (1000px x 1600px), allowing users to click and determine the x/y position within the full window ...

The file located at 'node_modules/minimatch/dist/cjs/index' does not contain an exported element called 'IMinimatch'. Perhaps you intended to reference 'Minimatch' instead?

I encountered an error while using rimraf as a devDependency (v5.0.0) in my project. The error message I received was: node_modules/@types/glob/index.d.ts:29:42 - error TS2694: Namespace '".../node_modules/minimatch/dist/cjs/index"' has ...

Obtain the template as a string within Vue

Let's examine the scenario of having a single file component in Vue with the following structure: // Article.vue <template> <div> <h1>{{title}}</h1> <p>{{body}}</p> </div> </template> If w ...

Utilizing jQuery to implement a CSS style with a fading effect, such as FadeIn()

I have a jQuery script that applies a CSS style to an HTML table row when the user clicks on a row link: $(this).closest('tr').css("background-color", "silver"); Is there a way to soften this color change effect, such as by gradually fading in ...

Expanding a string by adding numeric characters using JavaScript Regular Expressions

Can you increment a numeric substring using regex/replace? For example, if the end of a string (like window location) contains #img-{digit}, is it possible to use regex to replace the digit with +1? I know how to match the hash, but extracting the number, ...

Avian-themed masking feature for jCarousel

Currently, I have a series of images in constant motion using jCarousel. Only one image is visible fully at any given time, and I am looking to create a "feathering" effect on the edges of the carousel so that the images smoothly fade in and out as they co ...

Is there a way to bring in both a variable and a type from a single file in Typescript?

I have some interfaces and an enum being exported in my implementation file. // types/user.ts export enum LoginStatus { Initial = 0, Authorized = 1, NotAuthorized = 2, } export interface UserState { name: string; loginStatus: LoginStatus; }; ex ...

The functionality of loading JSON is not working properly in ePub3

Currently, I am working on a project involving the creation of an ePub3 eBook. One of the exciting features I have successfully integrated is three.js to showcase some models. My next goal is to develop 'hotspot' elements (small cubes that users ...

Is there a way to configure MaterialUI XGrid filters to target and filter by the renderCell parameters instead of the backend data source?

While utilizing MaterialUI XGrid to showcase rows of information, I am facing an issue with filtering. Currently, filtering can only be done based on the backend row values rather than what is displayed in the cell. For instance, consider a column named U ...

Use Express 4.x to automatically redirect HTTP traffic to HTTPS

Here is the code snippet that I am working with: var https = require('https'); var http = require('http'); var express = require('express'); var app = express(); var router = express.Router() ...

What steps should I take to successfully install using npm if I keep encountering the same error?

Every time I attempt to install a package using npm, I encounter the following warning: npm WARN EBADENGINE Unsupported engine { npm WARN EBADENGINE package: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c7b3aeaba2b3be ...

Transforming a two-column text document into a set of key-value pairs

Recently, I've been diving into the world of Node.js and Express. My current challenge involves converting a text file into key-value pairs. The approach I'm taking with my staircase program is as follows: https://i.sstatic.net/GPs200IQ.png Th ...

Choose only one option from the dropdown menu at a time within the specified div

I attempted to utilize the "setSelected" option on my multiselect feature, but I noticed that it does not function with div elements (at least I could not make it work myself). I am endeavoring to create two synchronized multiselects using this example: b ...

The function is not explicitly declared within the instance, yet it is being cited during the rendering process in a .vue

import PageNav from '@/components/PageNav.vue'; import PageFooter from '@/components/PageFooter.vue'; export default { name: 'Groups', components: { PageNav, PageFooter, }, data() { return { groups: ...

Creating a unique, random output while maintaining a log of previous results

While working on a recent project, I found myself in need of a custom Javascript function that could generate random numbers within a specified range without any repetitions until all possibilities were exhausted. Since such a function didn't already ...