1। পরিচিতি
শেষ আপডেট: 2020-09-11
আপনি কি নির্মাণ করবেন
এই কোডল্যাবে, আমরা কৌণিক এবং ফায়ারবেস সহ একটি ওয়েব কানবান বোর্ড তৈরি করব! আমাদের চূড়ান্ত অ্যাপে তিনটি বিভাগের কাজ থাকবে: ব্যাকলগ, প্রগতিতে এবং সম্পূর্ণ। আমরা ড্র্যাগ অ্যান্ড ড্রপ ব্যবহার করে কাজগুলি তৈরি করতে, মুছতে এবং এক বিভাগ থেকে অন্য বিভাগে স্থানান্তর করতে সক্ষম হব।
আমরা কৌণিক ব্যবহার করে ইউজার ইন্টারফেস ডেভেলপ করব এবং ফায়ারস্টোরকে আমাদের স্থায়ী স্টোর হিসেবে ব্যবহার করব। কোডল্যাবের শেষে আমরা কৌণিক CLI ব্যবহার করে ফায়ারবেস হোস্টিং-এ অ্যাপটি স্থাপন করব।
আপনি কি শিখবেন
- কৌণিক উপাদান এবং সিডিকে কীভাবে ব্যবহার করবেন।
- কীভাবে আপনার কৌণিক অ্যাপে ফায়ারবেস ইন্টিগ্রেশন যোগ করবেন।
- ফায়ারস্টোরে আপনার ক্রমাগত ডেটা কীভাবে রাখবেন।
- একটি একক কমান্ডের সাহায্যে কৌণিক CLI ব্যবহার করে Firebase হোস্টিং-এ কীভাবে আপনার অ্যাপ স্থাপন করবেন।
আপনি কি প্রয়োজন হবে
এই কোডল্যাব ধরে নেয় যে আপনার কাছে একটি Google অ্যাকাউন্ট আছে এবং Angular এবং Angular CLI-এর প্রাথমিক ধারণা রয়েছে।
চল শুরু করি!
2. একটি নতুন প্রকল্প তৈরি করা
প্রথমে, আসুন একটি নতুন কৌণিক কর্মক্ষেত্র তৈরি করি:
ng new kanban-fire
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
এই ধাপে কয়েক মিনিট সময় লাগতে পারে। কৌণিক CLI আপনার প্রকল্প কাঠামো তৈরি করে এবং সমস্ত নির্ভরতা ইনস্টল করে। ইনস্টলেশন প্রক্রিয়া সম্পূর্ণ হলে, kanban-fire
ডিরেক্টরিতে যান এবং Angular CLI এর ডেভেলপমেন্ট সার্ভার শুরু করুন:
ng serve
http://localhost:4200 খুলুন এবং আপনার অনুরূপ একটি আউটপুট দেখতে হবে:
আপনার সম্পাদকে, src/app/app.component.html
খুলুন এবং এর সম্পূর্ণ সামগ্রী মুছুন৷ আপনি যখন http://localhost:4200 এ ফিরে যান তখন আপনাকে একটি ফাঁকা পৃষ্ঠা দেখতে হবে।
3. উপাদান এবং CDK যোগ করা
কৌণিক উপাদান ডিজাইন অনুগত ইউজার ইন্টারফেস উপাদানগুলির বাস্তবায়নের সাথে আসে @angular/material
প্যাকেজের অংশ। @angular/material
নির্ভরতাগুলির মধ্যে একটি হল কম্পোনেন্ট ডেভেলপমেন্ট কিট বা CDK। CDK আদিম প্রদান করে, যেমন a11y ইউটিলিটি, ড্র্যাগ অ্যান্ড ড্রপ এবং ওভারলে। আমরা @angular/cdk
প্যাকেজে CDK বিতরণ করি।
আপনার অ্যাপে উপাদান যোগ করতে চালান:
ng add @angular/material
এই কমান্ডটি আপনাকে একটি থিম বাছাই করতে বলে, যদি আপনি গ্লোবাল ম্যাটেরিয়াল টাইপোগ্রাফি শৈলী ব্যবহার করতে চান এবং আপনি যদি কৌণিক উপাদানের জন্য ব্রাউজার অ্যানিমেশন সেট আপ করতে চান। এই কোডল্যাবের মতো একই ফলাফল পেতে "ইন্ডিগো/পিঙ্ক" বেছে নিন এবং শেষ দুটি প্রশ্নের উত্তর "হ্যাঁ" দিয়ে দিন।
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>
এখানে, আমরা আমাদের উপাদান ডিজাইন থিমের প্রাথমিক রঙ ব্যবহার করে একটি টুলবার যোগ করি এবং এর ভিতরে আমরা " local_fire_depeartment
ফায়ার" লেবেলের পাশে স্থানীয়_ফায়ার_ডিপার্টমেন্ট আইকন ব্যবহার করি। আপনি যদি এখন আপনার কনসোলটি দেখেন, আপনি দেখতে পাবেন যে কৌণিক কয়েকটি ত্রুটি নিক্ষেপ করেছে। সেগুলি ঠিক করতে, নিশ্চিত করুন যে আপনি 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
এ সংশ্লিষ্ট মডিউলগুলি আমদানি করতে হবে।
স্ক্রিনে আপনার এখন নিম্নলিখিতগুলি দেখতে হবে:
HTML এর মাত্র 4 লাইন এবং দুটি আমদানির সাথে খারাপ নয়!
4. কাজগুলি ভিজ্যুয়ালাইজ করা
পরবর্তী পদক্ষেপ হিসাবে, আসুন একটি উপাদান তৈরি করি যা আমরা কানবান বোর্ডের কাজগুলি কল্পনা করতে ব্যবহার করতে পারি।
src/app
ডিরেক্টরিতে যান এবং নিম্নলিখিত CLI কমান্ডটি চালান:
ng generate component task
এই কমান্ডটি AppModule
তৈরি করে এবং TaskComponent
এর ঘোষণা যোগ করে। 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
খুলুন এবং নিম্নলিখিত 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
উপাদান ব্যবহার করছি, কিন্তু আমরা অ্যাপে এর সংশ্লিষ্ট মডিউল আমদানি করিনি। উপরে থেকে ত্রুটিটি ঠিক করতে, আমাদের MatCardModule
এ AppModule
আমদানি করতে হবে:
src/app/app.module.ts
...
import { MatCardModule } from '@angular/material/card';
@NgModule({
declarations: [
AppComponent
],
imports: [
...
MatCardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
এরপরে আমরা AppComponent-এ কয়েকটি কাজ তৈরি করব এবং 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. টাস্কের জন্য ড্র্যাগ অ্যান্ড ড্রপ বাস্তবায়ন করা
আমরা এখন মজার অংশের জন্য প্রস্তুত! আসুন তিনটি ভিন্ন রাজ্যের কাজগুলির জন্য তিনটি সাঁতারের পথ তৈরি করি এবং কৌণিক CDK ব্যবহার করে একটি টেনে-এন্ড-ড্রপ কার্যকারিতা প্রয়োগ করি৷
app.component.html
এ, উপরে *ngFor
নির্দেশ সহ app-task
উপাদানটি সরান এবং এটির সাথে প্রতিস্থাপন করুন:
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>
এখানে অনেক কিছু হচ্ছে. আসুন ধাপে ধাপে এই স্নিপেটের পৃথক অংশগুলি দেখি। এটি টেমপ্লেটের শীর্ষ-স্তরের কাঠামো:
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>
...
প্রথমত, আমরা swimlane কে একটি mat-card
হিসাবে সংজ্ঞায়িত করি, যা cdkDropList
নির্দেশিকা ব্যবহার করে। এই উপাদানটি যে শৈলী প্রদান করে তার কারণে আমরা একটি mat-card
ব্যবহার করি। cdkDropList
পরবর্তীতে আমাদেরকে এলিমেন্টের ভিতরে টাস্ক ড্রপ করতে দেবে। আমরা নিম্নলিখিত দুটি ইনপুট সেট করেছি:
-
cdkDropListData
- ড্রপ তালিকার ইনপুট যা আমাদের ডেটা অ্যারে নির্দিষ্ট করতে দেয় -
cdkDropListConnectedTo
- বর্তমানcdkDropList
সংযুক্ত অন্যান্যcdkDropList
এর রেফারেন্স। এই ইনপুটটি সেট করে আমরা নির্দিষ্ট করি যে অন্য কোন তালিকায় আমরা আইটেম ড্রপ করতে পারি
উপরন্তু, আমরা cdkDropListDropped
আউটপুট ব্যবহার করে ড্রপ ইভেন্ট পরিচালনা করতে চাই। একবার cdkDropList
এই আউটপুটটি নির্গত করলে, আমরা AppComponent
এর ভিতরে ঘোষিত drop
পদ্ধতিটি চালু করব এবং বর্তমান ইভেন্টটিকে একটি যুক্তি হিসাবে পাস করব।
লক্ষ্য করুন যে আমরা এই ধারকটির শনাক্তকারী হিসাবে ব্যবহার করার জন্য একটি id
এবং একটি class
নামও নির্দিষ্ট করি যাতে আমরা এটিকে স্টাইল করতে পারি। এখন দেখা যাক mat-card
বিষয়বস্তু শিশুদের। আমাদের সেখানে দুটি উপাদান রয়েছে:
- একটি অনুচ্ছেদ, যা আমরা "খালি তালিকা" টেক্সট দেখানোর জন্য ব্যবহার করি যখন
todo
তালিকায় কোনো আইটেম নেই -
app-task
কম্পোনেন্ট। লক্ষ্য করুন যে এখানে আমরা তালিকার নাম এবং$event
অবজেক্টের সাথেeditTask
পদ্ধতিতে কল করার মাধ্যমে প্রাথমিকভাবে ঘোষণা করাedit
আউটপুট পরিচালনা করছি। এটি আমাদের সঠিক তালিকা থেকে সম্পাদিত টাস্ক প্রতিস্থাপন করতে সাহায্য করবে। এর পরে, আমরা উপরের মত করেtodo
তালিকায় পুনরাবৃত্তি করি এবং আমরাtask
ইনপুট পাস করি। এইবার, যাইহোক, আমরাcdkDrag
নির্দেশিকাও যোগ করি। এটি পৃথক কাজগুলিকে টেনে আনা যায়।
এই সমস্ত কাজ করার জন্য, আমাদের app.module.ts আপডেট করতে হবে এবং 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
পদ্ধতিতে আমরা প্রথমে পরীক্ষা করি যে আমরা যে তালিকা থেকে টাস্কটি আসছে সেই তালিকায় ড্রপ করছি। যদি তাই হয়, তাহলে আমরা অবিলম্বে ফিরে আসি। অন্যথায়, আমরা বর্তমান টাস্কটি গন্তব্যের সাঁতারুতে স্থানান্তর করি।
ফলাফল হওয়া উচিত:
এই মুহুর্তে আপনি ইতিমধ্যে দুটি তালিকার মধ্যে আইটেম স্থানান্তর করতে সক্ষম হওয়া উচিত!
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
অ্যারেতে টাস্ক যোগ করি।
এটি কাজ করে তা নিশ্চিত করতে, আমাদের প্রথমে MatDialogModule
এ AppModule
আমদানি করতে হবে:
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
জন্য দুটি ক্ষেত্র সহ একটি ফর্ম তৈরি করি। ব্যবহারকারী ডায়ালগ খোলে আমরা স্বয়ংক্রিয়ভাবে title
ইনপুট ফোকাস করতে cdkFocusInput
নির্দেশিকা ব্যবহার করি।
লক্ষ্য করুন কিভাবে টেমপ্লেটের ভিতরে আমরা কম্পোনেন্টের 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
পাস করি।
দুটি প্রকার রয়েছে যা আমরা উল্লেখ করেছি কিন্তু এখনও ঘোষণা করিনি - 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
সেট করে অনুভূমিকভাবে সারিবদ্ধ করা হয়েছে। এর পরে, ফ্লেক্সবক্স ব্যবহার করে, আমরা সাঁতারের পথগুলি একে অপরের পাশে রাখি এবং অবশেষে আমরা কীভাবে কাজ এবং খালি তালিকাগুলিকে কল্পনা করি তাতে কিছু সমন্বয় করি।
একবার আপনার অ্যাপ পুনরায় লোড হয়ে গেলে, আপনি নিম্নলিখিত ব্যবহারকারী ইন্টারফেসটি দেখতে পাবেন:
যদিও আমরা আমাদের অ্যাপের শৈলীগুলি উল্লেখযোগ্যভাবে উন্নত করেছি, আমরা যখন কাজগুলিকে ঘুরিয়ে নিয়ে যাই তখনও আমাদের একটি বিরক্তিকর সমস্যা থাকে:
যখন আমরা "দুধ কিনুন" টাস্কটি টেনে আনতে শুরু করি, আমরা একই কাজের জন্য দুটি কার্ড দেখতে পাই - একটি আমরা টেনে নিয়ে যাচ্ছি এবং একটি সাঁতার কাটার মধ্যে৷ কৌণিক CDK আমাদেরকে CSS শ্রেণীর নাম সরবরাহ করে যা আমরা এই সমস্যাটি সমাধান করতে ব্যবহার করতে পারি।
src/app/app.component.css
এর নীচে নিম্নলিখিত স্টাইল ওভাররাইডগুলি যোগ করুন:
src/app/app.component.css
.cdk-drag-animating {
transition: transform 250ms;
}
.cdk-drag-placeholder {
opacity: 0;
}
যখন আমরা একটি উপাদান টেনে আনছি, তখন কৌণিক CDK এর ড্র্যাগ এবং ড্রপ এটিকে ক্লোন করে এবং এটিকে সেই অবস্থানে সন্নিবেশিত করে যেখানে আমরা আসলটি ড্রপ করতে যাচ্ছি। এই উপাদানটি দৃশ্যমান নয় তা নিশ্চিত করার জন্য আমরা cdk-drag-placeholder
ক্লাসে অপাসিটি প্রপার্টি সেট করি, যা CDK প্লেসহোল্ডারে যোগ করতে চলেছে।
উপরন্তু, যখন আমরা একটি উপাদান ড্রপ করি, তখন 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. একটি নতুন ফায়ারবেস প্রকল্প তৈরি করা
এখন, একটি নতুন ফায়ারবেস প্রকল্প তৈরি করা যাক!
- ফায়ারবেস কনসোলে যান।
- "কানবানফায়ার" নামে একটি নতুন প্রকল্প তৈরি করুন।
10. প্রকল্পে Firebase যোগ করা
এই বিভাগে আমরা আমাদের প্রকল্পকে Firebase-এর সাথে একীভূত করব! ফায়ারবেস টিম @angular/fire
প্যাকেজ অফার করে, যা দুটি প্রযুক্তির মধ্যে একীকরণ প্রদান করে। আপনার অ্যাপে Firebase সমর্থন যোগ করতে আপনার কর্মক্ষেত্রের রুট ডিরেক্টরি খুলুন এবং চালান:
ng add @angular/fire
এই কমান্ডটি @angular/fire
প্যাকেজ ইনস্টল করে এবং আপনাকে কয়েকটি প্রশ্ন জিজ্ঞাসা করে। আপনার টার্মিনালে, আপনার এমন কিছু দেখতে হবে:
ইতিমধ্যে, ইনস্টলেশন একটি ব্রাউজার উইন্ডো খোলে যাতে আপনি আপনার Firebase অ্যাকাউন্ট দিয়ে প্রমাণীকরণ করতে পারেন। অবশেষে, এটি আপনাকে একটি ফায়ারবেস প্রকল্প বেছে নিতে বলে এবং আপনার ডিস্কে কিছু ফাইল তৈরি করে।
এর পরে, আমাদের একটি ফায়ারস্টোর ডাটাবেস তৈরি করতে হবে! "ক্লাউড ফায়ারস্টোর" এর অধীনে "ডেটাবেস তৈরি করুন" এ ক্লিক করুন।
এর পরে, পরীক্ষা মোডে একটি ডাটাবেস তৈরি করুন:
অবশেষে, একটি অঞ্চল নির্বাচন করুন:
আপনার পরিবেশে ফায়ারবেস কনফিগারেশন যোগ করাই এখন বাকি। আপনি Firebase কনসোলে আপনার প্রকল্পের কনফিগারেশন খুঁজে পেতে পারেন।
- প্রজেক্ট ওভারভিউ এর পাশে গিয়ার আইকনে ক্লিক করুন।
- প্রকল্প সেটিংস নির্বাচন করুন.
"আপনার অ্যাপস" এর অধীনে একটি "ওয়েব অ্যাপ" নির্বাচন করুন:
এরপর, আপনার আবেদন নিবন্ধন করুন এবং নিশ্চিত করুন যে আপনি "Firebase হোস্টিং" সক্ষম করেছেন :
আপনি "অ্যাপ নিবন্ধন করুন" ক্লিক করার পরে, আপনি আপনার কনফিগারেশনটি 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. ফায়ারস্টোরে ডেটা সরানো
এখন যেহেতু আমরা Firebase SDK সেট আপ করেছি, আসুন আমাদের ডেটা Firestore-এ সরানোর জন্য @angular/fire
ব্যবহার করি! প্রথমে, 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 {}
যেহেতু আমরা ফায়ারস্টোর ব্যবহার করব, তাই আমাদের AngularFirestore
AppComponent
অ্যাঙ্গুলারফায়ারস্টোরকে ইনজেক্ট করতে হবে:
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
একটি অ্যারের পরিবর্তে একটি পর্যবেক্ষণযোগ্য প্রদান করে, এবং এছাড়াও আমরা নির্দিষ্ট করি যে এই সংগ্রহের নথিগুলির জন্য id ক্ষেত্রটিকে 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
পদ্ধতিটি আপডেট করা যাক! বন্ধ ডায়ালগ হ্যান্ডলারের ভিতরে, আমাদের কোডের নিম্নলিখিত লাইনগুলি পরিবর্তন করতে হবে:
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
এর টেমপ্লেট আপডেট করতে হবে। inProgress
todo
এবং done
বৈশিষ্ট্যের প্রতিটি অ্যাক্সেসকে টোডো todo | async
দিয়ে প্রতিস্থাপন করুন todo | async
, inProgress | async
, এবং done | async
যথাক্রমে done | async
.
অ্যাসিঙ্ক পাইপ স্বয়ংক্রিয়ভাবে সংগ্রহের সাথে সম্পর্কিত পর্যবেক্ষণযোগ্য সাবস্ক্রাইব করে। যখন পর্যবেক্ষণযোগ্য একটি নতুন মান নির্গত করে, তখন কৌণিক স্বয়ংক্রিয়ভাবে পরিবর্তন সনাক্তকরণ চালায় এবং নির্গত অ্যারে প্রক্রিয়া করে।
উদাহরণ স্বরূপ, আসুন আমরা 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
নির্দেশে ডেটা পাস করি তখন আমরা async পাইপ প্রয়োগ করি। এটি *ngIf
নির্দেশের ভিতরেও একই, তবে মনে রাখবেন যে সেখানে আমরা ঐচ্ছিক চেইনিং ব্যবহার করি (এটি অ্যাঙ্গুলারে নিরাপদ নেভিগেশন অপারেটর হিসাবেও পরিচিত), length
বৈশিষ্ট্য অ্যাক্সেস করার সময় নিশ্চিত করার জন্য যে আমরা কাজ করলে todo | async
null
বা undefined
নয়।
এখন আপনি যখন ইউজার ইন্টারফেসে একটি নতুন টাস্ক তৈরি করবেন এবং ফায়ারস্টোর খুলবেন, তখন আপনার এরকম কিছু দেখতে হবে:
12. আশাবাদী আপডেট উন্নত করা
অ্যাপ্লিকেশনটিতে আমরা বর্তমানে আশাবাদী আপডেটগুলি সম্পাদন করছি। ফায়ারস্টোরে আমাদের সত্যের উৎস রয়েছে, কিন্তু একই সময়ে আমাদের কাছে কাজগুলির স্থানীয় অনুলিপি রয়েছে; যখন সংগ্রহের সাথে সম্পর্কিত যেকোন পর্যবেক্ষণযোগ্য পদার্থ নির্গত হয়, আমরা কাজগুলির একটি অ্যারে পাই। যখন কোনো ব্যবহারকারীর ক্রিয়া রাষ্ট্রকে পরিবর্তিত করে, আমরা প্রথমে স্থানীয় মান আপডেট করি এবং তারপর পরিবর্তনটিকে Firestore-এ প্রচার করি।
যখন আমরা একটি টাস্ককে একটি সাঁতারের পথ থেকে অন্য সাঁতারের পথে নিয়ে যাই, তখন আমরা TransferArrayItem আহ্বান করি 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 হোস্টিং-এ আপনার অ্যাপ স্থাপন করুন।
- একটি URL আউটপুট যাতে আপনি ফলাফলের পূর্বরূপ দেখতে পারেন।
14. অভিনন্দন
অভিনন্দন, আপনি সফলভাবে অ্যাঙ্গুলার এবং ফায়ারবেস সহ একটি কানবান বোর্ড তৈরি করেছেন!
আপনি তিনটি কলাম সহ একটি ব্যবহারকারী ইন্টারফেস তৈরি করেছেন যা বিভিন্ন কাজের স্থিতি উপস্থাপন করে। কৌণিক CDK ব্যবহার করে, আপনি কলাম জুড়ে টাস্ক এবং ড্রপ প্রয়োগ করেছেন। তারপর, কৌণিক উপাদান ব্যবহার করে, আপনি নতুন কাজ তৈরি করতে এবং বিদ্যমানগুলি সম্পাদনা করার জন্য একটি ফর্ম তৈরি করেছেন৷ এরপর, আপনি শিখেছেন কিভাবে @angular/fire
ব্যবহার করতে হয় এবং সমস্ত অ্যাপ্লিকেশন স্টেট Firestore-এ সরানো হয়েছে। অবশেষে, আপনি আপনার অ্যাপ্লিকেশন ফায়ারবেস হোস্টিং-এ স্থাপন করেছেন।
এরপর কি?
মনে রাখবেন যে আমরা পরীক্ষা কনফিগারেশন ব্যবহার করে অ্যাপ্লিকেশন স্থাপন করেছি। প্রোডাকশনে আপনার অ্যাপ স্থাপন করার আগে আপনি সঠিক অনুমতি সেট আপ করেছেন তা নিশ্চিত করুন। আপনি এখানে কিভাবে এটি করতে শিখতে পারেন .
বর্তমানে, আমরা একটি নির্দিষ্ট সাঁতার কাটার পৃথক কাজের ক্রম সংরক্ষণ করি না। এটি বাস্তবায়ন করতে, আপনি টাস্ক ডকুমেন্টে একটি অর্ডার ক্ষেত্র ব্যবহার করতে পারেন এবং এটির উপর ভিত্তি করে সাজান।
উপরন্তু, আমরা শুধুমাত্র একজন একক ব্যবহারকারীর জন্য কানবান বোর্ড তৈরি করেছি, যার অর্থ হল যে অ্যাপটি খুলবে তাদের জন্য আমাদের কাছে একটি একক কানবান বোর্ড রয়েছে। আপনার অ্যাপের বিভিন্ন ব্যবহারকারীদের জন্য পৃথক বোর্ড প্রয়োগ করতে, আপনাকে আপনার ডাটাবেস গঠন পরিবর্তন করতে হবে। Firestore-এর সর্বোত্তম অনুশীলন সম্পর্কে এখানে জানুন।