Shady98
Shady98

Reputation: 21

Angular @Input decorator and other issues

I am making the Kanban-fire app by following the tutorial on Google Codelabs. This is the link of the part where I'm facing issues. https://developers.google.com/codelabs/building-a-web-app-with-angular-and-firebase#10

I'm using Angular CLI 11.0.7, Node 12.18.2, Ubuntu 20.10 64bit, TypeScript 4.1.2.

As explained in the tutorial, I followed along and made all the necessary changes. Here's the issue that I'm facing.

At first, I was getting this error

Property 'task' has no initializer and is not definitely assigned in the constructor. 19 task: Task;

and this is my corresponding code

@Input() task: Task;

It uses the following Task interface

export interface Task {
  id?: string;
  title: string;
  description: string;
}

I tried to solve this error by initializing the task property in @Input as

@Input() task: Task = {title: 'def title', description: 'def desc'};

and it was all good until these errors arose.

As you'll be able to see in the link that I've shared, that we have to make changes to app.component.html as well as some methods in app.component.ts file. I made the changes and now I'm getting these errors.

Error: src/app/app.component.html:21:34 - error TS2345: Argument of type 'CdkDragDrop<{ id: string; }[] | null, any>' is not assignable to parameter of type 'CdkDragDrop<Task[], Task[]>'.
  Type '{ id: string; }[] | null' is not assignable to type 'Task[]'.
    Type 'null' is not assignable to type 'Task[]'.

21       (cdkDropListDropped)="drop($event)"

and

Error occurs in the template of component AppComponent.
src/app/app.component.html:27:17 - error TS2739: Type '{ id: string; }' is missing the following properties from type 'Task': title, description

27         cdkDrag [task]="task"></app-task>

For complete reference, I am providing app.component.html as well as app.component.ts code here

<mat-toolbar color="primary">
  <mat-icon>local_fire_department</mat-icon>
  <span>Kanban Fire</span>
</mat-toolbar>

<div class="content-wrapper">
  <button (click)="newTask()" mat-button>
    <mat-icon>add</mat-icon> Add Task
  </button>
</div>

<div class="container-wrapper">
  <div class="container">
    <h2>Backlog</h2>
    <mat-card
      cdkDropList
      id="todo"
      #todoList="cdkDropList"
      [cdkDropListData]="todo | async"
      [cdkDropListConnectedTo]="[doneList, inProgressList]"
      (cdkDropListDropped)="drop($event)"
      class="list">
      <p class="empty-label" *ngIf="(todo | async)?.length === 0">Empty List</p>
      <app-task
        (edit)="editTask('todo', $event)"
        *ngFor="let task of todo | async"
        cdkDrag [task]="task"></app-task>
    </mat-card>
  </div>

  <div class="container">
    <h2>In Progress</h2>
    <mat-card
      cdkDropList
      id="inProgress"
      #inProgressList="cdkDropList"
      [cdkDropListData]="inProgress | async"
      [cdkDropListConnectedTo]="[todoList, doneList]"
      (cdkDropListDropped)="drop($event)"
      class="list">
      <p class="empty-label" *ngIf="(inProgress | async)?.length === 0">Empty List</p>
      <app-task
        (edit)="editTask('inProgress', $event)"
        *ngFor="let task of inProgress | async"
        cdkDrag [task]="task"></app-task>
    </mat-card>
  </div>

  <div class="container">
    <h2>Done</h2>
    <mat-card
      cdkDropList
      id="done"
      #doneList="cdkDropList"
      [cdkDropListData]="done | async"
      [cdkDropListConnectedTo]="[todoList, inProgressList]"
      (cdkDropListDropped)="drop($event)"
      class="list">
      <p class="empty-label" *ngIf="(done | async)?.length === 0">Empty List</p>
      <app-task
        (edit)="editTask('done', $event)"
        *ngFor="let task of done | async"
        cdkDrag [task]=""></app-task>
    </mat-card>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core';
import { Task } from '../app/task/task';
import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';
import { MatDialog } from '@angular/material/dialog';
import { TaskDialogComponent } from '../app/task-dialog/task-dialog.component';
import { TaskDialogResult } from '../app/task-dialog/task-dialog.component';
import { AngularFirestore } from '@angular/fire/firestore';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todo = this.store.collection('todo').valueChanges({ idField: 'id' });
  inProgress = this.store.collection('inProgress').valueChanges({ idField: 'id' });
  done = this.store.collection('done').valueChanges({ idField: 'id' });

  constructor(private dialog: MatDialog, private store: AngularFirestore) {}

  editTask(list: 'done' | 'todo' | 'inProgress', task: Task): void {
    const dialogRef = this.dialog.open(TaskDialogComponent, {
      width: '270px',
      data: {
        task,
        enableDelete: true,
      }
    });
    dialogRef.afterClosed().subscribe((result: TaskDialogResult) => {
      if (result.delete) {
        this.store.collection(list).doc(task.id).delete();
      } else {
        this.store.collection(list).doc(task.id).update(task);
      }
    });
  }

  drop(event: CdkDragDrop<Task[]>): void {
    if (event.previousContainer === event.container) {
      return;
    }
    const item = event.previousContainer.data[event.previousIndex];
    this.store.firestore.runTransaction(() => {
      const promise = Promise.all([
        this.store.collection(event.previousContainer.id).doc(item.id).delete(),
        this.store.collection(event.container.id).add(item),
      ]);
      return promise;
    });
    transferArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }  
  
  newTask(): void {
    const dialogRef = this.dialog.open(TaskDialogComponent, {
      width: '270px',
      data: {
        task: {}
      }
    });
    dialogRef.afterClosed().subscribe((result: TaskDialogResult) => {
      this.store.collection('todo').add(result.task)
    });
  }
}

Any help will be appreciated.

Upvotes: 0

Views: 1638

Answers (2)

Denis Alarie 4RP
Denis Alarie 4RP

Reputation: 349

I had a similar problem in an application I was working on. I eventually paid close attention to my error message similar to yours ... your error message was --> "Property 'task' has no initializer and is not definitely assigned in the constructor. 19 task: Task;

In my case my error was -->"Property 'election' has no initializer and is not definitely assigned in the constructor.ts(2564)"

my input statement was

@Input() election: Election

and I added this line in my constructor which was formerly not populated with this initialization.

 this.election =ELECTIONS[0]

this solved my problem

I guess you have to initialize in the constructor whatever it is you are trying to import.

In summary source Component -->

elections-main-list.component.html

    <app-election-item
    
    *ngFor="let election of elections"
    
    [election]="election" >
    </app-election-item>

Import component

election-item-component.ts
export  class ElectionItemComponent implements OnInit{
 
  @Input() election: Election
 
  constructor() {
    this.election =ELECTIONS[0]
    
   }


As I  have only been using Angular for a short period of time the lesson I learned was to pay close attention to the error message. anyway this solution worked perfectly and on the next step.... 

Upvotes: 0

Shady98
Shady98

Reputation: 21

Thanks to @Eliseo for pointing this out. The strict mode being turned on caused all this to happen.

Just set strict and strictTemplates flags to false in tsconfig.json file.

Upvotes: 1

Related Questions