Angular और Firebase के साथ वेब ऐप्लिकेशन बनाना

1. परिचय

पिछला अपडेट: 11-09-2020

आप क्या बनाते हैं

इस कोडलैब में हम #39 हमारे फ़ाइनल ऐप्लिकेशन में तीन तरह के टास्क होंगे: बैकलॉग, काम जारी है, और पूरा हो गया है. हम खींचें और छोड़ें का इस्तेमाल करके, टास्क बना पाएंगे, उन्हें मिटा पाएंगे, और उन्हें एक कैटगरी से दूसरी कैटगरी में ट्रांसफ़र कर पाएंगे.

हम Angular का इस्तेमाल करके यूज़र इंटरफ़ेस बढ़ाएंगे और अपने स्थायी स्टोर के तौर पर Firestore का इस्तेमाल करेंगे. कोडलैब के आखिर में, हम Angular CLI का इस्तेमाल करके, ऐप्लिकेशन को Firebase होस्टिंग के लिए डिप्लॉय करेंगे.

b23bd3732d0206b.png

आप क्या #39;जानेंगे

  • ऐंगुलर सामग्री और सीडीके का इस्तेमाल करने का तरीका.
  • अपने Angular ऐप्लिकेशन में Firebase एकीकरण को जोड़ने का तरीका.
  • Firestore में लगातार डेटा बनाए रखने का तरीका.
  • एक कमांड की मदद से Angular CLI का इस्तेमाल करके, अपने ऐप्लिकेशन को Firebase होस्टिंग के लिए डिप्लॉय करें.

आपको क्या चाहिए

इस कोडलैब से यह माना जाता है कि आपके पास Google खाता है और Angular और Angular CLI की बुनियादी जानकारी है.

शुरू करें!

2. नया प्रोजेक्ट बनाना

सबसे पहले, आइए एक नया Angular फ़ाइल फ़ोल्डर बनाएं:

ng new kanban-fire
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS

इस प्रक्रिया में कुछ मिनट लग सकते हैं. Angular CLI आपके प्रोजेक्ट का स्ट्रक्चर बनाता है और सभी डिपेंडेंसी इंस्टॉल करता है. इंस्टॉल करने की प्रक्रिया पूरी होने के बाद, kanban-fire डायरेक्ट्री पर जाएं और Angular CLI's डेवलपमेंट सर्वर शुरू करें:

ng serve

http://localhost:4200 खोलें और आपको इससे मिलता-जुलता आउटपुट दिखेगा:

5ed77bc5b1109bf3.png

अपने एडिटर में src/app/app.component.html खोलें और इसका पूरा कॉन्टेंट मिटाएं. http://localhost:4200 पर वापस जाने पर, आपको एक खाली पेज दिखेगा.

3. Material and the CDK जोड़ना

Angular, @angular/material पैकेज के हिस्से के तौर पर, मटीरियल डिज़ाइन के साथ काम करने वाले यूज़र इंटरफ़ेस के कॉम्पोनेंट को लागू करने का तरीका है. @angular/material की डिपेंडेंसी में से एक, कॉम्पोनेंट डेवलपमेंट किट या सीडीके है. सीडीके ने प्रिमिटिव, जैसे कि A11y यूटिलिटी, ड्रैग ऐंड ड्रॉप, और ओवरले की सुविधा दी है. हम सीडीके को @angular/cdk पैकेज में बांटते हैं.

अपने ऐप्लिकेशन को चलाने के लिए सामग्री जोड़ने के लिए:

ng add @angular/material

इस निर्देश में आपसे कोई थीम चुनने के लिए कहा जाता है. अगर आप ग्लोबल मटीरियल टाइपोग्राफ़ी स्टाइल का इस्तेमाल करना चाहते हैं और आपको ऐंगुलर मटीरियल के लिए ब्राउज़र ऐनिमेशन सेट अप करना है, तो आपको यह विकल्प चुनना होगा. इस कोडलैब के समान नतीजा पाने के लिए, &Intgo/Ininkgo/Pink&kot; को चुनें और पिछले दो सवालों के जवाब &&कोटेशन की सुविधा से दें.

ng add निर्देश, @angular/material, उसकी डिपेंडेंसी इंस्टॉल करता है, और BrowserAnimationsModule को AppModule में इंपोर्ट करता है. अगले चरण में, हम इस मॉड्यूल के कॉम्पोनेंट का इस्तेमाल करना शुरू कर सकते हैं!

पहले, चलिए AppComponent में एक टूलबार और एक आइकॉन जोड़ते हैं. app.component.html खोलें और यह मार्कअप जोड़ें:

src/app/app.component.html

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

यहां, हम अपनी मटीरियल डिज़ाइन थीम के मुख्य रंग का इस्तेमाल करके एक टूलबार जोड़ते हैं. साथ ही, हम लेबल &Kanban Fire.&के कोटेशन के आगे local_fire_depeartment आइकॉन का इस्तेमाल करते हैं; अब अगर आप अपने कंसोल पर देखेंगे, तो आप देखेंगे कि Angular कुछ गड़बड़ियां देता है. उन्हें ठीक करने के लिए, पक्का करें कि आप AppModule में ये इंपोर्ट जोड़ते हैं:

src/app/app.module.ts

...
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    MatToolbarModule,
    MatIconModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

हम ऐंगुलर सामग्री के टूलबार और आइकॉन का इस्तेमाल करते हैं, इसलिए हमें AppModule में उससे जुड़े मॉड्यूल इंपोर्ट करने होंगे.

अब आपको स्क्रीन पर यह जानकारी दिखेगी:

a39cf8f8428a03bc.png

एचटीएमएल की सिर्फ़ चार लाइन और दो इंपोर्ट का इस्तेमाल करना अच्छा नहीं है!

4. टास्क विज़ुअलाइज़ करना

अगले चरण में, आइए एक ऐसा कॉम्पोनेंट बनाएं जिसे हम कंबन बोर्ड में मौजूद टास्क को विज़ुअलाइज़ करने के लिए इस्तेमाल कर सकते हैं.

src/app डायरेक्ट्री पर जाएं और यह सीएलआई कमांड चलाएं:

ng generate component task

यह निर्देश, TaskComponent को जनरेट करता है और AppModule में इसका एलान करता है. task डायरेक्ट्री में, task.ts नाम की एक फ़ाइल बनाएं. हम इस फ़ाइल का इस्तेमाल, कंबन बोर्ड के टास्क के इंटरफ़ेस को तय करने के लिए करेंगे. हर टास्क में एक वैकल्पिक id, title, और description फ़ील्ड होंगे, जिनमें सभी टाइप का स्ट्रिंग होगा:

src/app/task/task.ts

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

अब task.component.ts को अपडेट करें. हम चाहते हैं कि TaskComponent, Task टाइप के ऑब्जेक्ट के इनपुट के तौर पर स्वीकार करे. साथ ही, हम चाहते हैं कि यह & &कोटेशनedit&कोटेशन का आउटपुट दे:

src/app/task/task.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Task } from './task';

@Component({
  selector: 'app-task',
  templateUrl: './task.component.html',
  styleUrls: ['./task.component.css']
})
export class TaskComponent {
  @Input() task: Task | null = null;
  @Output() edit = new EventEmitter<Task>();
}

TaskComponent के टेंप्लेट में बदलाव करें! task.component.html को खोलें और इसके कॉन्टेंट को नीचे दिए गए एचटीएमएल से बदलें:

src/app/task/task.component.html

<mat-card class="item" *ngIf="task" (dblclick)="edit.emit(task)">
  <h2>{{ task.title }}</h2>
  <p>
    {{ task.description }}
  </p>
</mat-card>

ध्यान दें कि हमें अब कंसोल में गड़बड़ियां मिल रही हैं:

'mat-card' is not a known element:
1. If 'mat-card' is an Angular component, then verify that it is part of this module.
2. If 'mat-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.ng

ऊपर दिए गए टेंप्लेट में हम '@angular/material से mat-card घटक का इस्तेमाल कर रहे हैं, लेकिन हमने ऐप्लिकेशन में उससे जुड़ा मॉड्यूल इंपोर्ट नहीं किया है. ऊपर से दी गई गड़बड़ी को ठीक करने के लिए, हमें AppModule में MatCardModule इंपोर्ट करना होगा:

src/app/app.module.ts

...
import { MatCardModule } from '@angular/material/card';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    MatCardModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

इसके बाद, हम AppComponent में कुछ टास्क बनाएंगे और उन्हें TaskComponent का इस्तेमाल करके विज़ुअलाइज़ करेंगे!

AppComponent में, todo नाम की एक कैटगरी तय करें और इसमें दो टास्क जोड़ें:

src/app/app.component.ts

...
import { Task } from './task/task';

@Component(...)
export class AppComponent {
  todo: Task[] = [
    {
      title: 'Buy milk',
      description: 'Go to the store and buy milk'
    },
    {
      title: 'Create a Kanban app',
      description: 'Using Firebase and Angular create a Kanban app!'
    }
  ];
}

अब, app.component.html के नीचे यह *ngFor निर्देश जोड़ें:

src/app/app.component.html

<app-task *ngFor="let task of todo" [task]="task"></app-task>

ब्राउज़र खोलने पर, आपको यह जानकारी दिखेगी:

d96fccd13c63ceb1.png

5. टास्क के लिए, खींचें और छोड़ें की सुविधा को लागू करना

हम अब #39; मज़ेदार हिस्से के लिए तैयार हैं! आइए तीन अलग-अलग स्थितियों के लिए तीन तैराकी लेन-देन बनाते हैं. साथ ही, ऐंगुलर सीडीके का इस्तेमाल करके, खींचें और छोड़ें की सुविधा लागू करें.

app.component.html में, app-task कॉम्पोनेंट को सबसे ऊपर *ngFor डायरेक्टिव से हटाएं और इसे इससे बदलें:

src/app/app.component.html

<div class="container-wrapper">
  <div class="container">
    <h2>Backlog</h2>

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

  <div class="container">
    <h2>In progress</h2>

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

  <div class="container">
    <h2>Done</h2>

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

यहां बहुत कुछ #39 है. आइए, इस स्निपेट के अलग-अलग हिस्सों को सिलसिलेवार तरीके से देखते हैं. यह टेंप्लेट का टॉप-लेवल स्ट्रक्चर है:

src/app/app.component.html

...
<div class="container-wrapper">
  <div class="container">
    <h2>Backlog</h2>
    ...
  </div>

  <div class="container">
    <h2>In progress</h2>
    ...
  </div>

  <div class="container">
    <h2>Done</h2>
    ...
  </div>
</div>

यहां हम div बनाते हैं, जो तीनों तैराकी लेन्स को रैप करता है, जिसमें क्लास का नाम &कोटेशनcontainer-wrapper होता है. हर कोट के पास एक क्लास का नाम &कोटेशन,container&kot; और h2 टैग का एक शीर्षक होता है.

आइए, अब पहले तैराकी की संरचना पर नज़र डालें:

src/app/app.component.html

...
    <div class="container">
      <h2>Backlog</h2>

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

सबसे पहले, हम तैराकी के इलाके को mat-card के तौर पर परिभाषित करते हैं, जो cdkDropList डायरेक्टिव का इस्तेमाल करता है. इस कॉम्पोनेंट में मौजूद स्टाइल की वजह से, हम mat-card का इस्तेमाल करते हैं. cdkDropList में हम बाद में एलिमेंट के अंदर के टास्क छोड़ देंगे. हम यहां दिए गए दो इनपुट भी सेट करते हैं:

  • cdkDropListData - ड्रॉप सूची का इनपुट, जो हमें डेटा अरे बताने की अनुमति देता है
  • cdkDropListConnectedTo - मौजूदा cdkDropList से जुड़े अन्य cdkDropList के रेफ़रंस. इस इनपुट को सेट करके हम यह बताते हैं कि हम किन दूसरी सूचियों में आइटम छोड़ सकते हैं

इसके अलावा, हम cdkDropListDropped आउटपुट का इस्तेमाल करके ड्रॉप इवेंट को मैनेज करना चाहते हैं. जब cdkDropList यह आउटपुट देता है, तो हम AppComponent में बताए गए drop तरीके को चालू करते हैं और मौजूदा इवेंट को आर्ग्युमेंट के तौर पर पास करते हैं.

ध्यान दें कि हम इस कंटेनर के लिए, आइडेंटिफ़ायर के तौर पर इस्तेमाल करने के लिए id, और class का नाम भी तय करते हैं, ताकि हम इसे स्टाइल में ढाल सकें. अब mat-card के बच्चों के कॉन्टेंट को देखते हैं. हमारे पास दो एलिमेंट हैं:

  • एक पैराग्राफ़, जिसे हम todo सूची में कोई आइटम न होने पर &कोटेशन;खाली सूची&कोटेशन; टेक्स्ट दिखाने के लिए इस्तेमाल करते हैं
  • app-task कॉम्पोनेंट. ध्यान दें कि यहां हम&#39: edit आउटपुट को हैंडल कर रहे हैं, जिसका एलान हमने मूल रूप से editTask तरीके को सूची और $event ऑब्जेक्ट के नाम से कॉल करके किया था. इससे हमें बदलाव किए गए टास्क को सही सूची से बदलने में मदद मिलेगी. इसके बाद, हमने ऊपर बताई गई todo सूची को फिर से लागू किया है. साथ ही, हम task इनपुट को पास करते हैं. हालांकि, इस बार हम cdkDrag डायरेक्टिव भी जोड़ते हैं. इससे हर टास्क को एक जगह से खींचकर दूसरी जगह ले जाया जा सकता है.

यह सभी काम करने के लिए, हमें app.module.ts को अपडेट करना होगा और DragDropModule में आयात शामिल करना होगा:

src/app/app.module.ts

...
import { DragDropModule } from '@angular/cdk/drag-drop';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    DragDropModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

हमें editTask और drop के साथ-साथ, inProgress और done कैटगरी का भी एलान करना होगा:

src/app/app.component.ts

...
import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';

@Component(...)
export class AppComponent {
  todo: Task[] = [...];
  inProgress: Task[] = [];
  done: Task[] = [];

  editTask(list: string, task: Task): void {}

  drop(event: CdkDragDrop<Task[]|null>): void {
    if (event.previousContainer === event.container) {
      return;
    }
    if (!event.container.data || !event.previousContainer.data) {
      return;
    }
    transferArrayItem(
      event.previousContainer.data,
      event.container.data,
      event.previousIndex,
      event.currentIndex
    );
  }
}

ध्यान दें कि drop तरीके में हम पहले यह जांचते हैं कि हम' उसी सूची में छोड़ रहे हैं या नहीं जिससे टास्क आ रहा है. अगर ऐसा है, तो हम तुरंत ही वापस लौट जाएंगे. ऐसा न करने पर, हम मौजूदा टास्क को डेस्टिनेशन स्विमिंग पूल में ट्रांसफ़र कर देते हैं.

नतीजा यह होना चाहिए:

460f86bcd10454cf.png

इस समय आपको पहले से ही दोनों सूचियों के बीच आइटम स्थानांतरित करने में सक्षम होना चाहिए!

6. नए टास्क बनाना

अब, नए टास्क बनाने के लिए एक फ़ंक्शन का इस्तेमाल करें. इस उद्देश्य के लिए, AppComponent का टेंप्लेट अपडेट करें:

src/app/app.component.html

<mat-toolbar color="primary">
...
</mat-toolbar>

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

  <div class="container-wrapper">
    <div class="container">
      ...
    </div>
</div>

हम container-wrapper के आस-पास टॉप-लेवल का div एलिमेंट बनाते हैं और &कोटेशनaddऔर कोट; सामग्री वाले आइकॉन के साथ वाला बटन &कोटेशन जोड़ें.&कोटेशन का बटन जोड़ते हैं; बटन को तैराकी की सूची में सबसे ऊपर रखने के लिए हमें और रैपर चाहिए, जिसे हम फ़्लेक्सबॉक्स का इस्तेमाल करके एक-दूसरे के बगल में रखते हैं. यह बटन मटीरियल बटन कॉम्पोनेंट का इस्तेमाल करता है, इसलिए हमें AppModule में इससे जुड़े मॉड्यूल को इंपोर्ट करना होगा:

src/app/app.module.ts

...
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

अब, AppComponent में काम जोड़ने के लिए, फ़ंक्शन की सुविधा लागू करें. हम कॉन्टेंट के डायलॉग का इस्तेमाल करेंगे. डायलॉग में हम'फ़ॉर्म में दो फ़ील्ड होंगे: शीर्षक और विवरण. जब उपयोगकर्ता &कोटेशन जोड़ें; कोटेशन दें बटन

आइए,AppComponent में इस फ़ंक्शन को बेहतर तरीके से लागू करने के बारे में जानें:

src/app/app.component.ts

...
import { MatDialog } from '@angular/material/dialog';

@Component(...)
export class AppComponent {
  ...

  constructor(private dialog: MatDialog) {}

  newTask(): void {
    const dialogRef = this.dialog.open(TaskDialogComponent, {
      width: '270px',
      data: {
        task: {},
      },
    });
    dialogRef
      .afterClosed()
      .subscribe((result: TaskDialogResult|undefined) => {
        if (!result) {
          return;
        }
        this.todo.push(result.task);
      });
  }
}

हम ऐसे कंस्ट्रक्टर का एलान करते हैं जिसमें MatDialog क्लास इंजेक्ट करते हैं. newTask में हम:

  • TaskDialogComponent का इस्तेमाल करके एक नया डायलॉग खोलें, जिसे हम थोड़ी देर में तय करेंगे.
  • बताएं कि हम डायलॉग बॉक्स की चौड़ाई 270px. रखना चाहते हैं
  • खाली टास्क को डायलॉग के तौर पर डेटा के तौर पर पास करें. TaskDialogComponent में हम #33 इस डेटा ऑब्जेक्ट का रेफ़रंस पा सकेंगे.
  • हम क्लोज़ इवेंट की सदस्यता लेते हैं और result ऑब्जेक्ट से todo अरे में टास्क जोड़ते हैं.

यह पक्का करने के लिए, हमें पहले AppModule में MatDialogModule इंपोर्ट करना होगा:

src/app/app.module.ts

...
import { MatDialogModule } from '@angular/material/dialog';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    MatDialogModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

अब TaskDialogComponent को बनाएं. src/app डायरेक्ट्री पर जाएं और चलाएं:

ng generate component task-dialog

इसके फ़ंक्शन को लागू करने के लिए: src/app/task-dialog/task-dialog.component.html खोलें और इसकी सामग्री को इनमें से बदलें:

src/app/task-dialog/task-dialog.component.html

<mat-form-field>
  <mat-label>Title</mat-label>
  <input matInput cdkFocusInitial [(ngModel)]="data.task.title" />
</mat-form-field>

<mat-form-field>
  <mat-label>Description</mat-label>
  <textarea matInput [(ngModel)]="data.task.description"></textarea>
</mat-form-field>

<div mat-dialog-actions>
  <button mat-button [mat-dialog-close]="{ task: data.task }">OK</button>
  <button mat-button (click)="cancel()">Cancel</button>
</div>

ऊपर दिए गए टेंप्लेट में हम title और description के लिए दो फ़ील्ड वाला एक फ़ॉर्म बनाते हैं. जब उपयोगकर्ता डायलॉग खोलता है, तो हम cdkFocusInput निर्देश का इस्तेमाल अपने-आप title फ़ोकस में लाने के लिए करते हैं.

ध्यान दें कि हम टेंप्लेट के अंदर कॉम्पोनेंट की data प्रॉपर्टी का रेफ़रंस कैसे देते हैं. यह वही data होगा जिसे हम AppComponent में dialog के open तरीके को पास करते हैं. जब उपयोगकर्ता, फ़ील्ड के साथ काम करने वाले फ़ील्ड का कॉन्टेंट बदलता है, तो हम ngModel के साथ दो-तरफ़ा डेटा बाइंडिंग का इस्तेमाल करते हैं. इससे, शीर्षक और जानकारी में बदलाव किया जा सकता है.

जब उपयोगकर्ता 'ठीक है' बटन पर क्लिक करता है, तब हम अपने-आप { task: data.task } नतीजे दिखाते हैं. यह वह टास्क है जिसे हमने ऊपर दिए गए टेंप्लेट के फ़ॉर्म फ़ील्ड का इस्तेमाल करके बदला है.

अब, आइए कॉम्पोनेंट का कंट्रोलर लागू करते हैं:

src/app/task-dialog/task-dialog.component.ts

import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Task } from '../task/task';

@Component({
  selector: 'app-task-dialog',
  templateUrl: './task-dialog.component.html',
  styleUrls: ['./task-dialog.component.css'],
})
export class TaskDialogComponent {
  private backupTask: Partial<Task> = { ...this.data.task };

  constructor(
    public dialogRef: MatDialogRef<TaskDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: TaskDialogData
  ) {}

  cancel(): void {
    this.data.task.title = this.backupTask.title;
    this.data.task.description = this.backupTask.description;
    this.dialogRef.close(this.data);
  }
}

TaskDialogComponent में हम डायलॉग का रेफ़रंस इंजेक्ट करते हैं, ताकि हम उसे बंद कर सकें. साथ ही, हम MAT_DIALOG_DATA टोकन से जुड़ी सेवा देने वाली कंपनी की वैल्यू भी इंजेक्ट कर सकते हैं. यह वह डेटा ऑब्जेक्ट है जिसे हमने ऊपर AppComponent में ओपन मेथड में पास किया था. हम निजी प्रॉपर्टी backupTask का भी एलान करते हैं, जो उस डेटा की कॉपी है जिसे हमने डेटा ऑब्जेक्ट के साथ पास किया था.

जब उपयोगकर्ता 'रद्द करें' बटन दबाता है, तो हम this.data.task की उन प्रॉपर्टी को भी बदल देते हैं जो शायद बदल गई हैं. ऐसा करने पर, हम this.data से नतीजे के तौर पर पास होने वाले डायलॉग को बंद कर देंगे.

हमने दो तरह के रेफ़रंस दिए हैं, लेकिन वे अभी तक ##39 नहीं बता पाए - TaskDialogData और TaskDialogResult. src/app/task-dialog/task-dialog.component.ts के अंदर इन एलानों को फ़ाइल के नीचे जोड़ें:

src/app/task-dialog/task-dialog.component.ts

...
export interface TaskDialogData {
  task: Partial<Task>;
  enableDelete: boolean;
}

export interface TaskDialogResult {
  task: Task;
  delete?: boolean;
}

फ़ंक्शन तैयार होने से पहले, हमें AppModule में कुछ मॉड्यूल इंपोर्ट करने होंगे!

src/app/app.module.ts

...
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
    MatInputModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

जब आप अभी &कोटेशन जोड़ें;कोटेशन जोड़ें बटन पर क्लिक करें, तो आपको यह यूज़र इंटरफ़ेस दिखेगा:

33bcb987fade2a87.png

7. ऐप्लिकेशन के स्टाइल को बेहतर बनाना

हम ऐप्लिकेशन को और आकर्षक बनाने के लिए, इसके लेआउट में थोड़ा बदलाव करके, इसके लेआउट को बेहतर बना रहे हैं. हम स्विम लेन्स को एक-दूसरे के बगल में रखना चाहते हैं. हम &कीवर्ड का छोटा सा समायोजन भी करना चाहते हैं; &कार्य टैब जोड़ें बटन और खाली सूची लेबल.

src/app/app.component.css खोलें और नीचे दिए गए स्टाइल को नीचे जोड़ें:

src/app/app.component.css

mat-toolbar {
  margin-bottom: 20px;
}

mat-toolbar > span {
  margin-left: 10px;
}

.content-wrapper {
  max-width: 1400px;
  margin: auto;
}

.container-wrapper {
  display: flex;
  justify-content: space-around;
}

.container {
  width: 400px;
  margin: 0 25px 25px 0;
}

.list {
  border: solid 1px #ccc;
  min-height: 60px;
  border-radius: 4px;
}

app-new-task {
  margin-bottom: 30px;
}

.empty-label {
  font-size: 2em;
  padding-top: 10px;
  text-align: center;
  opacity: 0.2;
}

ऊपर दिए गए स्निपेट में, हम टूलबार के लेआउट और उसके लेबल को एडजस्ट करते हैं. हम यह भी पक्का करते हैं कि कॉन्टेंट को चौड़ाई में 1400px पर सेट किया गया हो और इसके मार्जिन को auto पर सेट किया गया हो. इसके बाद, फ़्लेक्सबॉक्स का इस्तेमाल करके, हम एक-दूसरे के बगल में स्विम लेन डालते हैं. साथ ही, टास्क और खाली सूचियों को दिखाने के तरीके में कुछ बदलाव करते हैं.

ऐप्लिकेशन फिर से लोड होने के बाद, आपको यह यूज़र इंटरफ़ेस दिखेगा:

69225f0b1aa5cb50.png

हालांकि, हमने अपने ऐप्लिकेशन के

f9aae712027624af.png

जब हम &कोटेशन को खींचना शुरू करते हैं, तब हम एक ही टास्क के लिए दो कार्ड बनाते हैं - पहला, हम खींचें और छोड़ें. साथ ही, दूसरे को तैराकी के रास्ते पर छोड़ दें. Angular CDK हमें सीएसएस क्लास के नाम देता है, जिनका इस्तेमाल करके हम इस समस्या को ठीक कर सकते हैं.

नीचे दिए गए स्टाइल को src/app/app.component.css के नीचे जोड़ें:

src/app/app.component.css

.cdk-drag-animating {
  transition: transform 250ms;
}

.cdk-drag-placeholder {
  opacity: 0;
}

जब हम किसी एलिमेंट को खींचते हैं, तो Angular CDK's क्लोन खींचें और छोड़ें और इसे उस जगह पर डाल दें जहां हम मूल इमेज को छोड़ना चाहते हैं. यह एलिमेंट नहीं दिख रहा है, यह पक्का करने के लिए हम cdk-drag-placeholder क्लास में ओपैसिटी प्रॉपर्टी सेट करते हैं, जिसे सीडीके प्लेसहोल्डर में जोड़ने जा रहा है.

साथ ही, जब हम किसी एलिमेंट को छोड़ते हैं, तो सीडीके cdk-drag-animating क्लास जोड़ता है. एलिमेंट को सीधे स्नैप करने के बजाय स्मूद ऐनिमेशन दिखाने के लिए, हम ट्रांज़िशन के लिए एक 250ms तय करते हैं.

हम अपने टास्क के स्टाइल में भी कुछ छोटे-मोटे बदलाव करना चाहते हैं. task.component.css में होस्ट होस्ट एलिमेंट's को block पर सेट करें और कुछ मार्जिन सेट करें:

src/app/task/task.component.css

:host {
  display: block;
}

.item {
  margin-bottom: 10px;
  cursor: pointer;
}

8. मौजूदा टास्क में बदलाव करना और उन्हें मिटाना

मौजूदा टास्क को हटाने और उनमें बदलाव करने के लिए, हम उनमें से ज़्यादातर फ़ंक्शन को दोबारा इस्तेमाल कर रहे हैं! जब उपयोगकर्ता किसी टास्क पर दो बार क्लिक करता है, तो हम TaskDialogComponent को खोलते हैं और फ़ॉर्म में दो फ़ील्ड के टास्क और #39; title और description में अपने-आप जानकारी भर देते हैं.

TaskDialogComponent में हम यूआरएल #39; हटाएं बटन भी जोड़ेंगे. जब उपयोगकर्ता इस पर क्लिक करेगा, तब हम'मिटाने के निर्देश को पास करेगा, जो AppComponent पर खत्म होगा.

TaskDialogComponent में हमें सिर्फ़ अपने टेंप्लेट में बदलाव करना होगा:

src/app/task-dialog/task-dialog.component.html

<mat-form-field>
 ...
</mat-form-field>

<div mat-dialog-actions>
  ...
  <button
    *ngIf="data.enableDelete"
    mat-fab
    color="primary"
    aria-label="Delete"
    [mat-dialog-close]="{ task: data.task, delete: true }">
    <mat-icon>delete</mat-icon>
  </button>
</div>

यह बटन मिटाएं सामग्री का आइकॉन दिखाता है. जब उपयोगकर्ता इस पर क्लिक करता है, तब हम डायलॉग बॉक्स को बंद कर देते हैं और नतीजे के तौर पर { task: data.task, delete: true } लिटरल ऑब्जेक्ट को पास करते हैं. यह भी ध्यान रखें कि हम mat-fab का इस्तेमाल करके बटन को गोलाकार बनाते हैं, इसका रंग प्राइमरी के तौर पर सेट करते हैं, और सिर्फ़ तब दिखाते हैं, जब डायलॉग डेटा को मिटाया जाता है.

बदलाव करने और मिटाने की सुविधाएं बाकी AppComponent में लागू होती हैं. अपने editTask तरीके को नीचे दिए गए तरीके से बदलें:

src/app/app.component.ts

@Component({ ... })
export class AppComponent {
  ...
  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|undefined) => {
      if (!result) {
        return;
      }
      const dataList = this[list];
      const taskIndex = dataList.indexOf(task);
      if (result.delete) {
        dataList.splice(taskIndex, 1);
      } else {
        dataList[taskIndex] = task;
      }
    });
  }
  ...
}

editTask मेथड के आर्ग्युमेंट देखें:

  • 'done' | 'todo' | 'inProgress', टाइप की सूची, जो अलग-अलग तैराकी के साथ जुड़ी प्रॉपर्टी से जुड़ी वैल्यू वाला स्ट्रिंग लिटरल टाइप है.
  • मौजूदा टास्क में हम बदलाव करना चाहते हैं.

मेथड' के मुख्य हिस्से में, हम पहले TaskDialogComponent का एक इंस्टेंस खोलते हैं. data जैसे ही हम एक ऑब्जेक्ट लिटरल पास करते हैं, जो उस टास्क के बारे में बताता है जिसमें हम बदलाव करना चाहते हैं. साथ ही, यह enableDelete प्रॉपर्टी को true पर सेट करके फ़ॉर्म में बदलाव करें बटन को भी चालू करता है.

जब हमें डायलॉग से नतीजा मिलता है, तो हम दो स्थितियों को हैंडल करते हैं:

  • जब delete फ़्लैग true पर सेट होता है (यानी, जब उपयोगकर्ता मिटाएं बटन दबाता है), तो हम टास्क को उससे जुड़ी सूची से हटा देते हैं.
  • इसके अलावा, हम दिए गए इंडेक्स के टास्क को सिर्फ़ उस टास्क से बदल देते हैं जो हमें डायलॉग नतीजे में मिला है.

9. नया Firebase प्रोजेक्ट बनाना

अब, एक नया Firebase प्रोजेक्ट बनाएं!

  • Firebase कंसोल पर जाएं.
  • "KanbanFire" नाम के साथ एक नया प्रोजेक्ट बनाएं.

10. प्रोजेक्ट में Firebase जोड़ना

इस सेक्शन में हम Firebase के साथ अपना प्रोजेक्ट इंटिग्रेट करेंगे! Firebase टीम @angular/fire पैकेज ऑफ़र करती है, जो इन दो टेक्नोलॉजी के बीच इंटिग्रेशन की सुविधा देती है. अपने ऐप्लिकेशन में Firebase सहायता जोड़ने के लिए, अपने फ़ाइल फ़ोल्डर की रूट डायरेक्ट्री खोलें और चलाएं:

ng add @angular/fire

यह निर्देश, @angular/fire पैकेज इंस्टॉल करता है और आपसे कुछ सवाल पूछता है. अपने टर्मिनल में, आपको कुछ ऐसा दिखेगा:

9ba88c0d52d18d0.png

इस दौरान, इंस्टॉलेशन ब्राउज़र विंडो खोलता है, ताकि आप अपने Firebase खाते से पुष्टि कर सकें. आखिर में, आपको Firebase प्रोजेक्ट चुनने और आपकी डिस्क पर कुछ फ़ाइलें बनाने के लिए कहा जाता है.

इसके बाद, हमें Firestore डेटाबेस बनाना होगा! & तरह का &Cloud Firestore;कोटेशन करें; क्लिक करें;डेटाबेस बनाएं.&कोटेशन;

1e4a08b5a2462956.png

इसके बाद, टेस्ट मोड में डेटाबेस बनाएं:

Ac1181b2c32049f9.png

आखिर में, कोई इलाका चुनें:

34bb94cc542a0597.png

अब बस आपको Firebase को अपने एनवायरमेंट में जोड़ना है. आप अपने प्रोजेक्ट कॉन्फ़िगरेशन को Firebase कंसोल में देख सकते हैं.

  • प्रोजेक्ट की खास जानकारी के आगे दिए गए गियर आइकॉन पर क्लिक करें.
  • प्रोजेक्ट सेटिंग चुनें.

c8253a20031de8a9.png

&अपने कोटेशन में जाकर, किसी &वेब ऐप्लिकेशन को चुनें;

428a1abcd0f90b23.png

इसके बाद, अपना ऐप्लिकेशन रजिस्टर करें और पक्का करें कि आपने &Firebase होस्टिंग&कोटेशन चालू किया हो:

586e44cb27dd8f39.png

&कोटेशन;ऐप्लिकेशन रजिस्टर करें&t> पर क्लिक करने के बाद, आप अपना कॉन्फ़िगरेशन src/environments/environment.ts में कॉपी कर सकते हैं:

e30f142d79cecf8f.png

आखिर में, आपकी कॉन्फ़िगरेशन फ़ाइल कुछ ऐसी दिखनी चाहिए:

src/environments/environment.ts

export const environment = {
  production: false,
  firebase: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};

11. डेटा को Firestore में ले जाना

अब जब हमने ##39;Firebase SDK टूल को सेट अप कर लिया है, तो हमें #39; @angular/fire का इस्तेमाल करके, हमारे डेटा को Firestore में ले जाने दें! सबसे पहले, आइए AppModule में उन मॉड्यूल को इंपोर्ट करें जिनकी हमें ज़रूरत है:

src/app/app.module.ts

...
import { environment } from 'src/environments/environment';
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';

@NgModule({
  declarations: [AppComponent, TaskDialogComponent, TaskComponent],
  imports: [
    ...
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

हम Firestore का इस्तेमाल कर रहे हैं, इसलिए हमें AppComponent's कंस्ट्रक्टर में AngularFirestore इंजेक्ट करना होगा:

src/app/app.component.ts

...
import { AngularFirestore } from '@angular/fire/firestore';

@Component({...})
export class AppComponent {
  ...
  constructor(private dialog: MatDialog, private store: AngularFirestore) {}
  ...
}

इसके बाद, हम स्विमलैन श्रेणियों को शुरू करने के तरीके को अपडेट करते हैं:

src/app/app.component.ts

...

@Component({...})
export class AppComponent {
  todo = this.store.collection('todo').valueChanges({ idField: 'id' }) as Observable<Task[]>;
  inProgress = this.store.collection('inProgress').valueChanges({ idField: 'id' }) as Observable<Task[]>;
  done = this.store.collection('done').valueChanges({ idField: 'id' }) as Observable<Task[]>;
  ...
}

यहां सीधे AngularFirestore संग्रह और#39; का कॉन्टेंट, डेटाबेस से पाने के लिए इस्तेमाल किया जाता है. ध्यान दें कि valueChanges से कैटगरी की बजाय ऑब्ज़र्व किया जा सकने वाला मिलता है. साथ ही, हम यह भी बताते हैं कि इस कलेक्शन में दस्तावेज़ों के लिए आईडी फ़ील्ड को id नाम दिया जाना चाहिए, ताकि उसका इस्तेमाल Task इंटरफ़ेस में किया जा सके. valueChanges के निगरानी में रखे गए ऑब्ज़र्व किए गए टास्क में, किसी भी समय बदलाव करने पर टास्क का कलेक्शन होता है.

हम श्रेणियों के बजाय ऑब्ज़र्व किए जा सकने वाले तरीके पर काम कर रहे हैं, इसलिए हमें टास्क जोड़ने, हटाने, और उनमें बदलाव करने के तरीके को अपडेट करना होगा. साथ ही, हमें स्विच के बीच टास्क को एक जगह से दूसरी जगह ले जाने के फ़ंक्शन को भी अपडेट करना होगा. अपनी इन-मेमोरी श्रेणियों में बदलाव करने के बजाय, हम डेटाबेस में डेटा को अपडेट करने के लिए Firebase SDK टूल का इस्तेमाल करेंगे.

सबसे पहले, आइए देखते हैं कि फिर से क्रम में लगाने का तरीका कैसा होता है. src/app/app.component.ts में drop वाला तरीका बदलें:

src/app/app.component.ts

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
  );
}

ऊपर दिए गए स्निपेट में, नया कोड हाइलाइट कर दिया गया है. किसी टास्क को मौजूदा स्विम लेन से टारगेट में ले जाने के लिए, हम टास्क को पहले कलेक्शन से हटाकर, दूसरे कलेक्शन में जोड़ देते हैं. हम दो तरह के काम करते हैं जिन्हें हम एक जैसा देखना चाहते हैं (जैसे कि ऑपरेशन को परमाणु बनाना). इसलिए, हम उन्हें Firestore लेन-देन में चलाते हैं.

इसके बाद, चलिए editTask Firestore का इस्तेमाल करने का तरीका अपडेट करते हैं! बंद डायलॉग हैंडलर के अंदर, हमें कोड की इन पंक्तियों को बदलना होगा:

src/app/app.component.ts

...
dialogRef.afterClosed().subscribe((result: TaskDialogResult|undefined) => {
  if (!result) {
    return;
  }
  if (result.delete) {
    this.store.collection(list).doc(task.id).delete();
  } else {
    this.store.collection(list).doc(task.id).update(task);
  }
});
...

हम उस टास्क से जुड़े टारगेट दस्तावेज़ को ऐक्सेस करते हैं, जिसे हम Firestore SDK का इस्तेमाल करके बदलते हैं. साथ ही, हम इसे मिटा देते हैं या इसे अपडेट कर देते हैं.

आखिर में, हमें नए टास्क बनाने के तरीके को अपडेट करना होगा. this.todo.push('task') को this.store.collection('todo').add(result.task) से बदलें.

ध्यान दें कि हमारे कलेक्शन अब कैटगरी नहीं हैं, बल्कि निगरानी किए जा सकते हैं. उन्हें विज़ुअलाइज़ करने के लिए हमें AppComponent के टेम्प्लेट को अपडेट करना होगा. बस todo, inProgress, और done प्रॉपर्टी के हर ऐक्सेस को todo | async, inProgress | async, और done | async से बदलें.

एक साथ काम नहीं करने वाली पाइप, अपने-आप कलेक्शन से जुड़ी निगरानी में रखे गए डिवाइस की सदस्यता लेती है. जब ऑब्ज़र्व किया जा सकने वाला कोई नया मान बनाता है, तो Angular बदलाव की पहचान अपने-आप कर लेता है और दूसरी श्रेणी में ले जाता है.

उदाहरण के लिए, आइए देखते हैं कि todo स्विम लेन-देन में हमें किस तरह के बदलाव करने होंगे:

src/app/app.component.html

<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>

जब हम cdkDropList डायरेक्टिव को डेटा पास करते हैं, तो हम एसिंक पाइप लागू करते हैं. यह *ngIf डायरेक्टिव में एक जैसा ही है. हालांकि, ध्यान रखें कि हम length प्रॉपर्टी को ऐक्सेस करते समय, वैकल्पिक चेन (जिसे Angular में सुरक्षित नेविगेशन ऑपरेटर भी कहा जाता है) का भी इस्तेमाल करते हैं. ऐसा इसलिए, ताकि todo | async या null न होने पर, हमें रनटाइम की गड़बड़ी न मिले.

अब जब आप यूज़र इंटरफ़ेस में नया टास्क बनाएंगे और Firestore खोलें, तो आपको कुछ ऐसा दिखेगा:

dd7ee20c0a10ebe2.png

12. सकारात्मक अपडेट में सुधार करना

ऐप्लिकेशन में फ़िलहाल हम अपडेट से जुड़े अपडेट कर रहे हैं. हमारे पास Firestore में सच्चाई का स्रोत है, लेकिन साथ ही, हमारे पास टास्क की स्थानीय कॉपी होती हैं; जब संग्रह से जुड़े किसी भी निगरानी वाले काम शुरू होते हैं, तो हमें कई तरह के टास्क मिलते हैं. जब कोई उपयोगकर्ता कार्रवाई, स्थिति में बदलाव करती है, तब हम पहले स्थानीय मानों को अपडेट करते हैं. इसके बाद, बदलावों को Firestore में लागू करते हैं.

जब हम किसी एक स्विम लेन-देन को दूसरे में ले जाते हैं, तो हम हर तैराकी समूह में काम को दिखाने वाली श्रेणियों के स्थानीय इंस्टेंस पर काम करने वाले transferArrayItem, को शुरू करते हैं. Firebase SDK टूल इन श्रेणियों को बदला नहीं जा सकता. इसका मतलब है कि अगली बार जब Angular बदलाव का पता लगाएगा, तब हम #{0/}

साथ ही, हम Firestore अपडेट को ट्रिगर करते हैं और Firebase SDK टूल सही वैल्यू वाले अपडेट को ट्रिगर करता है, इसलिए कुछ मिलीसेकंड में यूज़र इंटरफ़ेस अपनी सही स्थिति पर पहुंच जाएगा. इससे, वह टास्क ट्रांसफ़र हो जाता है जिसे हमने पहली सूची से अगली सूची पर भेजा. आप नीचे दिए गए GIF पर इसे अच्छी तरह से देख सकते हैं:

70b946eebfa6f316.gif

इस समस्या को हल करने का सही तरीका हर ऐप्लिकेशन के लिए अलग-अलग होता है, लेकिन सभी मामलों में हमें यह पक्का करना होगा कि जब तक हमारा डेटा अपडेट नहीं हो जाता, तब तक हम एक ही स्थिति बनाए रखते हैं.

हम BehaviorSubject का फ़ायदा ले सकते हैं, जो हमें valueChanges से मिलने वाले मूल ऑब्ज़र्वर को रैप कर देता है. हुड के तहत, BehaviorSubject एक बदली जा सकने वाली श्रेणी रखता है जो transferArrayItem से अपडेट रहता है.

समाधान लागू करने के लिए, हमें बस AppComponent को अपडेट करना होगा:

src/app/app.component.ts

...
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { BehaviorSubject } from 'rxjs';


const getObservable = (collection: AngularFirestoreCollection<Task>) => {
  const subject = new BehaviorSubject<Task[]>([]);
  collection.valueChanges({ idField: 'id' }).subscribe((val: Task[]) => {
    subject.next(val);
  });
  return subject;
};

@Component(...)
export class AppComponent {
  todo = getObservable(this.store.collection('todo')) as Observable<Task[]>;
  inProgress = getObservable(this.store.collection('inProgress')) as Observable<Task[]>;
  done = getObservable(this.store.collection('done')) as Observable<Task[]>;
...
}

ऊपर दिए गए स्निपेट में हम सिर्फ़ BehaviorSubject बनाते हैं. ऐसा करने से, कलेक्शन में होने वाले हर बदलाव की निगरानी करने से, एक वैल्यू बनती है.

सब कुछ उम्मीद के मुताबिक है, क्योंकि BehaviorSubject बदलाव का पता लगाने वाले शुरू होने पर श्रेणी को दोबारा इस्तेमाल करता है और सिर्फ़ तब अपडेट होता है, जब हमें Firestore से नया मान मिलता है.

13. ऐप्लिकेशन को डिप्लॉय किया जा रहा है

अपने ऐप्लिकेशन को डिप्लॉय करने के लिए, हमें बस इतना करना है:

ng deploy

यह निर्देश:

  1. अपने ऐप्लिकेशन को प्रोडक्शन कॉन्फ़िगरेशन के साथ बनाएं, कंपाइल-टाइम ऑप्टिमाइज़ेशन लागू करें.
  2. अपने ऐप्लिकेशन को Firebase होस्टिंग के लिए डिप्लॉय करें.
  3. यूआरएल का आउटपुट पाएं, ताकि आप नतीजे की झलक देख सकें.

14. बधाई हो

बधाई हो, आपने Angular और Firebase के साथ एक कंबान बोर्ड बनाया है!

आपने तीन कॉलम की मदद से एक यूज़र इंटरफ़ेस बनाया है, जिसमें अलग-अलग टास्क की स्थिति दिखाई गई है. Angular CDK का इस्तेमाल करके, आपने पूरे कॉलम में टास्क को खींचकर छोड़ा है. फिर, ऐंगुलर सामग्री का इस्तेमाल करके आपने नए टास्क बनाने और मौजूदा टास्क में बदलाव करने के लिए एक फ़ॉर्म बनाया. इसके बाद, आपने @angular/fire का इस्तेमाल करने और सभी ऐप्लिकेशन स्टेट को Firestore में ले जाने का तरीका सीखा. आखिर में, आपने अपने ऐप्लिकेशन को Firebase होस्टिंग के लिए डिप्लॉय किया.

आगे क्या होगा?

याद रखें कि हमने टेस्ट कॉन्फ़िगरेशन का इस्तेमाल करके ऐप्लिकेशन डिप्लॉय किया है. अपने ऐप्लिकेशन को प्रोडक्शन में डिप्लॉय करने से पहले पक्का करें कि आपने सही अनुमतियां सेट की हैं. ऐसा करने का तरीका यहां जानें.

फ़िलहाल, हम किसी खास स्विम लेन में अलग-अलग टास्क का क्रम सुरक्षित नहीं रखते हैं. इसे लागू करने के लिए, आप टास्क के दस्तावेज़ में ऑर्डर फ़ील्ड का इस्तेमाल कर सकते हैं और उसके हिसाब से क्रम तय कर सकते हैं.

इसके अलावा, हमने सिर्फ़ एक उपयोगकर्ता के लिए कंबान बोर्ड बनाया है. इसका मतलब है कि ऐप्लिकेशन खोलने वाले किसी भी व्यक्ति के लिए एक कंबन बोर्ड है. अपने ऐप्लिकेशन के अलग-अलग उपयोगकर्ताओं के लिए अलग-अलग बोर्ड लागू करने के लिए, आपको अपने डेटाबेस की संरचना बदलनी होगी. Firestore के #सबसे सही तरीकों के बारे में यहां जानें.