1. परिचय
पिछली बार अपडेट किया गया: 11-09-2020
आपको क्या बनाने को मिलेगा
इस कोडलैब में, हम Angular और Firebase की मदद से एक वेब कानबन बोर्ड बनाएंगे! हमारे फ़ाइनल ऐप्लिकेशन में, टास्क की तीन कैटगरी होंगी: बैकलॉग, प्रोसेस में हैं, और पूरे हो गए. हम टास्क बना सकेंगे, उन्हें मिटा सकेंगे, और उन्हें खींचकर छोड़ने की सुविधा का इस्तेमाल करके, एक कैटगरी से दूसरी कैटगरी में ट्रांसफ़र कर सकेंगे.
हम Angular का इस्तेमाल करके यूज़र इंटरफ़ेस डेवलप करेंगे. साथ ही, Firestore को अपने परसिस्टेंट स्टोर के तौर पर इस्तेमाल करेंगे. कोड लैब के आखिर में, हम Angular CLI का इस्तेमाल करके ऐप्लिकेशन को Firebase Hosting पर डिप्लॉय करेंगे.
आपको क्या सीखने को मिलेगा
- Angular Material और CDK का इस्तेमाल करने का तरीका.
- अपने 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
इस चरण में कुछ मिनट लग सकते हैं. ऐंगुलर सीएलआई, आपके प्रोजेक्ट का स्ट्रक्चर बनाता है और सभी डिपेंडेंसी इंस्टॉल करता है. इंस्टॉल करने की प्रोसेस पूरी होने के बाद, kanban-fire
डायरेक्ट्री पर जाएं और Angular CLI के डेवलपमेंट सर्वर को शुरू करें:
ng serve
http://localhost:4200 खोलें. आपको इससे मिलता-जुलता आउटपुट दिखेगा:
अपने एडिटर में, src/app/app.component.html
खोलें और इसका पूरा कॉन्टेंट मिटाएं. http://localhost:4200 पर वापस जाने पर, आपको एक खाली पेज दिखेगा.
3. Material और CDK को जोड़ना
Angular में, Material Design के मुताबिक यूज़र इंटरफ़ेस कॉम्पोनेंट लागू करने की सुविधा मिलती है. यह सुविधा, @angular/material
पैकेज का हिस्सा है. @angular/material
की डिपेंडेंसी में से एक, कॉम्पोनेंट डेवलपमेंट किट या सीडीके है. सीडीके, प्रिमिटिव उपलब्ध कराता है. जैसे, सुलभता से जुड़ी सुविधाएं, खींचकर छोड़ना, और ओवरले. हम @angular/cdk
पैकेज में सीडीके डिस्ट्रिब्यूट करते हैं.
ऐप्लिकेशन रन में मटीरियल जोड़ने के लिए:
ng add @angular/material
इस कमांड से आपको थीम चुनने के लिए कहा जाता है. ऐसा तब होता है, जब आपको ग्लोबल मटीरियल टाइपोग्राफ़ी स्टाइल का इस्तेमाल करना हो और Angular Material के लिए ब्राउज़र ऐनिमेशन सेट अप करने हों. इस कोडलैब में दिखाए गए नतीजे पाने के लिए, "इंडिगो/पिंक" चुनें. साथ ही, आखिरी दो सवालों के जवाब "हां" में दें.
ng add
कमांड, @angular/material
और इसकी डिपेंडेंसी इंस्टॉल करती है. साथ ही, AppModule
में BrowserAnimationsModule
इंपोर्ट करती है. अगले चरण में, हम इस मॉड्यूल के कॉम्पोनेंट का इस्तेमाल शुरू कर सकते हैं!
सबसे पहले, 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 { }
हम Angular मटीरियल टूलबार और आइकॉन का इस्तेमाल करते हैं. इसलिए, हमें AppModule
में इनसे जुड़े मॉड्यूल इंपोर्ट करने होंगे.
अब आपको स्क्रीन पर यह जानकारी दिखेगी:
सिर्फ़ चार लाइनों के एचटीएमएल और दो इंपोर्ट के साथ, यह काफ़ी अच्छा है!
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>
ब्राउज़र खोलने पर, आपको यह दिखेगा:
5. टास्क के लिए, खींचें और छोड़ें सुविधा लागू करना
अब हम इस वर्कशॉप के सबसे मज़ेदार हिस्से की तरफ़ चलते हैं! आइए, तीन अलग-अलग राज्यों के लिए तीन स्विमलेन बनाएं. साथ ही, Angular CDK का इस्तेमाल करके, ड्रैग-एंड-ड्रॉप की सुविधा लागू करें.
app.component.html
में, सबसे ऊपर *ngFor
डायरेक्टिव के साथ app-task
कॉम्पोनेंट को हटाएं और इसे इससे बदलें:
src/app/app.component.html
<div class="content-wrapper">
<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>
</div>
यहां बहुत कुछ हो रहा है. आइए, इस स्निपेट के हर हिस्से को एक-एक करके देखें. यह टेंप्लेट का टॉप-लेवल स्ट्रक्चर है:
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
" है और 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
कॉम्पोनेंट. ध्यान दें कि यहां हमनेedit
आउटपुट को हैंडल किया है. इसे हमने मूल रूप से, सूची के नाम और$event
ऑब्जेक्ट के साथeditTask
तरीके को कॉल करके घोषित किया था. इससे हमें, बदली गई जानकारी वाले टास्क को सही सूची में जोड़ने में मदद मिलेगी. इसके बाद, हम ऊपर की तरह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[]>): 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
तरीके में, हम सबसे पहले यह देखते हैं कि हम उसी सूची में ड्रॉप कर रहे हैं जिससे टास्क आ रहा है. अगर ऐसा होता है, तो हम तुरंत वापस आ जाते हैं. ऐसा न होने पर, हम मौजूदा टास्क को डेस्टिनेशन स्विमलेन में ट्रांसफ़र कर देते हैं.
नतीजा यह होना चाहिए:
अब आपको दोनों सूचियों के बीच आइटम ट्रांसफ़र करने का विकल्प दिखना चाहिए!
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
में टास्क जोड़ने की सुविधा लागू करते हैं. हम मटीरियल डायलॉग का इस्तेमाल करेंगे. डायलॉग बॉक्स में, हमारे पास दो फ़ील्ड वाला एक फ़ॉर्म होगा: टाइटल और जानकारी. जब उपयोगकर्ता "टास्क जोड़ें" बटन पर क्लिक करेगा, तब हम डायलॉग खोलेंगे. इसके बाद, जब उपयोगकर्ता फ़ॉर्म सबमिट करेगा, तब हम नई बनाई गई टास्क को todo
सूची में जोड़ देंगे.
आइए, 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
में, हमें इस डेटा ऑब्जेक्ट का रेफ़रंस मिल जाएगा. - हम क्लोज़ इवेंट के लिए सदस्यता लेते हैं और
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
में open तरीके से पास किया था. हम backupTask
प्राइवेट प्रॉपर्टी का भी एलान करते हैं. यह उस टास्क की कॉपी होती है जिसे हमने डेटा ऑब्जेक्ट के साथ पास किया था.
जब उपयोगकर्ता 'रद्द करें' बटन दबाता है, तो हम this.data.task
की बदली हुई प्रॉपर्टी को पहले जैसा कर देते हैं. साथ ही, हम डायलॉग बॉक्स को बंद कर देते हैं और this.data
को नतीजे के तौर पर पास कर देते हैं.
हमने दो टाइप का रेफ़रंस दिया है, लेकिन अब तक उन्हें तय नहीं किया है - 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 { }
अब "टास्क जोड़ें" बटन पर क्लिक करने पर, आपको यह यूज़र इंटरफ़ेस दिखेगा:
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
पर सेट करते हैं. इसके बाद, फ़्लेक्सबॉक्स का इस्तेमाल करके, हम स्विमलेन को एक-दूसरे के बगल में रखते हैं. आखिर में, हम टास्क और खाली सूचियों को विज़ुअलाइज़ करने के तरीके में कुछ बदलाव करते हैं.
ऐप्लिकेशन के फिर से लोड होने के बाद, आपको यह यूज़र इंटरफ़ेस दिखेगा:
हमने अपने ऐप्लिकेशन की स्टाइल को काफ़ी हद तक बेहतर बनाया है. हालांकि, अब भी हमें एक समस्या का सामना करना पड़ रहा है. यह समस्या तब होती है, जब हम टास्क को एक जगह से दूसरी जगह ले जाते हैं:
"दूध खरीदो" टास्क को खींचकर ले जाते समय, हमें एक ही टास्क के दो कार्ड दिखते हैं. एक वह जिसे हम खींचकर ले जा रहे हैं और दूसरा स्विमलेन में मौजूद कार्ड. 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 का ड्रैग ऐंड ड्रॉप फ़ीचर, उस एलिमेंट का क्लोन बना देता है. इसके बाद, उसे उस जगह पर डाल देता है जहां हमें ओरिजनल एलिमेंट को छोड़ना है. यह पक्का करने के लिए कि यह एलिमेंट न दिखे, हम cdk-drag-placeholder
क्लास में ओपैसिटी प्रॉपर्टी सेट करते हैं. सीडीके, इस क्लास को प्लेसहोल्डर में जोड़ेगा.
इसके अलावा, जब हम किसी एलिमेंट को छोड़ते हैं, तो CDK cdk-drag-animating
क्लास जोड़ता है. किसी एलिमेंट को सीधे तौर पर स्नैप करने के बजाय, स्मूद ऐनिमेशन दिखाने के लिए, हम 250ms
अवधि वाला ट्रांज़िशन तय करते हैं.
हम अपने टास्क की स्टाइल में कुछ छोटे-मोटे बदलाव भी करना चाहते हैं. task.component.css
में, होस्ट एलिमेंट के डिसप्ले को block
पर सेट करें और कुछ मार्जिन सेट करें:
src/app/task/task.component.css
:host {
display: block;
}
.item {
margin-bottom: 10px;
cursor: pointer;
}
8. मौजूदा टास्क में बदलाव करना और उन्हें मिटाना
मौजूदा टास्क में बदलाव करने और उन्हें हटाने के लिए, हम पहले से लागू की गई ज़्यादातर सुविधाओं का फिर से इस्तेमाल करेंगे! जब उपयोगकर्ता किसी टास्क पर दो बार क्लिक करता है, तब हम TaskDialogComponent
खोलेंगे. साथ ही, फ़ॉर्म में मौजूद दो फ़ील्ड में टास्क के title
और description
की जानकारी भरेंगे.
हम TaskDialogComponent
में, मिटाएं बटन भी जोड़ेंगे. जब उपयोगकर्ता इस पर क्लिक करेगा, तब हम मिटाने का निर्देश भेजेंगे. यह निर्देश 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 Console पर जाएं.
- "KanbanFire" नाम का नया प्रोजेक्ट बनाएं.
10. प्रोजेक्ट में Firebase जोड़ना
इस सेक्शन में, हम अपने प्रोजेक्ट को Firebase के साथ इंटिग्रेट करेंगे! Firebase टीम, @angular/fire
पैकेज उपलब्ध कराती है. इससे दोनों टेक्नोलॉजी को इंटिग्रेट किया जा सकता है. अपने ऐप्लिकेशन में Firebase की सुविधा जोड़ने के लिए, अपने वर्कस्पेस की रूट डायरेक्ट्री खोलें और यह कमांड चलाएं:
ng add @angular/fire
इस कमांड से @angular/fire
पैकेज इंस्टॉल हो जाता है और आपसे कुछ सवाल पूछे जाते हैं. आपको अपने टर्मिनल में कुछ ऐसा दिखेगा:
इस बीच, इंस्टॉलर एक ब्राउज़र विंडो खोलता है, ताकि आप अपने Firebase खाते से पुष्टि कर सकें. आखिर में, यह आपसे कोई Firebase प्रोजेक्ट चुनने के लिए कहता है और आपकी डिस्क पर कुछ फ़ाइलें बनाता है.
इसके बाद, हमें एक Firestore डेटाबेस बनाना होगा! "Cloud Firestore" में जाकर, "डेटाबेस बनाएं" पर क्लिक करें.
इसके बाद, टेस्ट मोड में डेटाबेस बनाएं:
आखिर में, कोई देश/इलाका चुनें:
अब सिर्फ़ Firebase कॉन्फ़िगरेशन को अपने एनवायरमेंट में जोड़ना बाकी है. आपको Firebase कंसोल में प्रोजेक्ट का कॉन्फ़िगरेशन दिखेगा.
- प्रोजेक्ट की खास जानकारी के बगल में मौजूद गियर आइकॉन पर क्लिक करें.
- प्रोजेक्ट सेटिंग चुनें.
"आपके ऐप्लिकेशन" में जाकर, कोई "वेब ऐप्लिकेशन" चुनें:
इसके बाद, अपने ऐप्लिकेशन को रजिस्टर करें और पक्का करें कि आपने "Firebase Hosting" को चालू किया हो:
"ऐप्लिकेशन रजिस्टर करें" पर क्लिक करने के बाद, अपने कॉन्फ़िगरेशन को src/environments/environment.ts
में कॉपी किया जा सकता है:
आखिर में, आपकी कॉन्फ़िगरेशन फ़ाइल ऐसी दिखनी चाहिए:
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 में ले जाना
हमने Firebase SDK टूल सेट अप कर लिया है. अब @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
के कंस्ट्रक्टर में 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
का इस्तेमाल करके, डेटाबेस से सीधे तौर पर कलेक्शन का कॉन्टेंट पाते हैं. ध्यान दें कि valueChanges
, ऐरे के बजाय observable दिखाता है. साथ ही, हम यह भी बताते हैं कि इस कलेक्शन में मौजूद दस्तावेज़ों के लिए आईडी फ़ील्ड को 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 लेन-देन में चलाते हैं.
अब, Firestore का इस्तेमाल करने के लिए, editTask
तरीके को अपडेट करते हैं! डायलॉग बंद करने वाले हैंडलर में, हमें कोड की इन लाइनों को बदलना होगा:
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
या undefined
नहीं है, तो हमें रनटाइम गड़बड़ी न मिले.
अब यूज़र इंटरफ़ेस में कोई नया टास्क बनाने और Firestore खोलने पर, आपको कुछ ऐसा दिखेगा:
12. ऑप्टिमिस्टिक अपडेट को बेहतर बनाना
फ़िलहाल, हम ऐप्लिकेशन में ऑप्टिमिस्टिक अपडेट कर रहे हैं. हमारे पास Firestore में डेटा का सोर्स है. हालांकि, हमारे पास टास्क की लोकल कॉपी भी हैं. जब कलेक्शन से जुड़े किसी भी ऑब्ज़र्वेबल से डेटा मिलता है, तो हमें टास्क की एक ऐरे मिलती है. जब उपयोगकर्ता की किसी कार्रवाई से स्थिति में बदलाव होता है, तो हम सबसे पहले स्थानीय वैल्यू अपडेट करते हैं. इसके बाद, बदलाव को Firestore पर भेजते हैं.
जब हम किसी टास्क को एक स्विमलेन से दूसरी स्विमलेन में ले जाते हैं, तो हम transferArrayItem,
को शुरू करते हैं. यह हर स्विमलेन में मौजूद टास्क को दिखाने वाली ऐरे के लोकल इंस्टेंस पर काम करता है. Firebase SDK, इन ऐरे को इम्यूटेबल के तौर पर देखता है. इसका मतलब है कि अगली बार जब Angular बदलाव का पता लगाएगा, तब हमें इनके नए इंस्टेंस मिलेंगे. इससे टास्क ट्रांसफ़र करने से पहले, पिछली स्थिति रेंडर हो जाएगी.
इसके साथ ही, हम Firestore अपडेट को ट्रिगर करते हैं. साथ ही, Firebase SDK टूल सही वैल्यू के साथ अपडेट को ट्रिगर करता है. इसलिए, कुछ मिलीसेकंड में यूज़र इंटरफ़ेस अपनी सही स्थिति में आ जाएगा. इससे, अभी ट्रांसफ़र किया गया टास्क पहली सूची से दूसरी सूची में चला जाता है. नीचे दिए गए 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
इस कमांड से:
- अपने ऐप्लिकेशन को प्रोडक्शन कॉन्फ़िगरेशन के साथ बनाएं. साथ ही, कंपाइल-टाइम ऑप्टिमाइज़ेशन लागू करें.
- अपने ऐप्लिकेशन को Firebase Hosting पर डिप्लॉय करें.
- एक यूआरएल जनरेट करो, ताकि हम नतीजे की झलक देख सकें.
14. बधाई हो
बधाई हो, आपने Angular और Firebase की मदद से Kanban बोर्ड बना लिया है!
आपने एक यूज़र इंटरफ़ेस बनाया है. इसमें तीन कॉलम हैं. ये अलग-अलग टास्क की स्थिति दिखाते हैं. आपने Angular CDK का इस्तेमाल करके, कॉलम में टास्क को खींचकर छोड़ने की सुविधा लागू की है. इसके बाद, आपने Angular Material का इस्तेमाल करके, नए टास्क बनाने और मौजूदा टास्क में बदलाव करने के लिए एक फ़ॉर्म बनाया. इसके बाद, आपने @angular/fire
का इस्तेमाल करना सीखा और ऐप्लिकेशन की सभी स्थितियों को Firestore में ले गए. आखिर में, आपने अपने ऐप्लिकेशन को Firebase Hosting पर डिप्लॉय किया.
आगे क्या करना है?
ध्यान दें कि हमने ऐप्लिकेशन को टेस्ट कॉन्फ़िगरेशन का इस्तेमाल करके डिप्लॉय किया है. अपने ऐप्लिकेशन को प्रोडक्शन में डिप्लॉय करने से पहले, पक्का करें कि आपने सही अनुमतियां सेट अप की हों. ऐसा करने का तरीका यहां बताया गया है.
फ़िलहाल, हम किसी स्विमलेन में मौजूद अलग-अलग टास्क के क्रम को बनाए नहीं रखते. इसे लागू करने के लिए, टास्क वाले दस्तावेज़ में ऑर्डर फ़ील्ड का इस्तेमाल किया जा सकता है. साथ ही, इसके आधार पर क्रम से लगाया जा सकता है.
इसके अलावा, हमने सिर्फ़ एक उपयोगकर्ता के लिए कैनबन बोर्ड बनाया है. इसका मतलब है कि ऐप्लिकेशन खोलने वाले हर व्यक्ति के लिए, हमारे पास एक ही कैनबन बोर्ड है. अगर आपको अपने ऐप्लिकेशन के अलग-अलग उपयोगकर्ताओं के लिए अलग-अलग बोर्ड लागू करने हैं, तो आपको अपने डेटाबेस स्ट्रक्चर में बदलाव करना होगा. Firestore इस्तेमाल करने के सबसे सही तरीकों के बारे में जानने के लिए, यहां जाएं.