কৌণিক এবং ফায়ারবেস দিয়ে একটি ওয়েব অ্যাপ্লিকেশন তৈরি করা

1। পরিচিতি

শেষ আপডেট: 2020-09-11

আপনি কি নির্মাণ করবেন

এই কোডল্যাবে, আমরা কৌণিক এবং ফায়ারবেস সহ একটি ওয়েব কানবান বোর্ড তৈরি করব! আমাদের চূড়ান্ত অ্যাপে তিনটি বিভাগের কাজ থাকবে: ব্যাকলগ, প্রগতিতে এবং সম্পূর্ণ। আমরা ড্র্যাগ অ্যান্ড ড্রপ ব্যবহার করে কাজগুলি তৈরি করতে, মুছতে এবং এক বিভাগ থেকে অন্য বিভাগে স্থানান্তর করতে সক্ষম হব।

আমরা কৌণিক ব্যবহার করে ইউজার ইন্টারফেস ডেভেলপ করব এবং ফায়ারস্টোরকে আমাদের স্থায়ী স্টোর হিসেবে ব্যবহার করব। কোডল্যাবের শেষে আমরা কৌণিক CLI ব্যবহার করে ফায়ারবেস হোস্টিং-এ অ্যাপটি স্থাপন করব।

b23bd3732d0206b.png

আপনি কি শিখবেন

  • কৌণিক উপাদান এবং সিডিকে কীভাবে ব্যবহার করবেন।
  • কীভাবে আপনার কৌণিক অ্যাপে ফায়ারবেস ইন্টিগ্রেশন যোগ করবেন।
  • ফায়ারস্টোরে আপনার ক্রমাগত ডেটা কীভাবে রাখবেন।
  • একটি একক কমান্ডের সাহায্যে কৌণিক 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 খুলুন এবং আপনার অনুরূপ একটি আউটপুট দেখতে হবে:

5ede7bc5b1109bf3.png

আপনার সম্পাদকে, 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 , এর নির্ভরতা ইনস্টল করে এবং BrowserAnimationsModuleAppModule আমদানি করে। পরবর্তী ধাপে, আমরা এই মডিউল অফার করা উপাদানগুলি ব্যবহার করা শুরু করতে পারি!

প্রথমে, 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 এ সংশ্লিষ্ট মডিউলগুলি আমদানি করতে হবে।

স্ক্রিনে আপনার এখন নিম্নলিখিতগুলি দেখতে হবে:

a39cf8f8428a03bc.png

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 উপাদান ব্যবহার করছি, কিন্তু আমরা অ্যাপে এর সংশ্লিষ্ট মডিউল আমদানি করিনি। উপরে থেকে ত্রুটিটি ঠিক করতে, আমাদের MatCardModuleAppModule আমদানি করতে হবে:

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>

আপনি যখন ব্রাউজার খুলবেন তখন আপনাকে নিম্নলিখিতগুলি দেখতে হবে:

d96fccd13c63ceb1.png

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 পদ্ধতিতে আমরা প্রথমে পরীক্ষা করি যে আমরা যে তালিকা থেকে টাস্কটি আসছে সেই তালিকায় ড্রপ করছি। যদি তাই হয়, তাহলে আমরা অবিলম্বে ফিরে আসি। অন্যথায়, আমরা বর্তমান টাস্কটি গন্তব্যের সাঁতারুতে স্থানান্তর করি।

ফলাফল হওয়া উচিত:

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 কাজ যোগ করার জন্য কার্যকারিতা বাস্তবায়ন করা যাক। আমরা একটি উপাদান ডায়ালগ ব্যবহার করব. ডায়ালগে আমাদের দুটি ক্ষেত্র সহ একটি ফর্ম থাকবে: শিরোনাম এবং বিবরণ। ব্যবহারকারী যখন "অ্যাড টাস্ক" বোতামে ক্লিক করেন তখন আমরা ডায়ালগ খুলব, এবং যখন ব্যবহারকারী ফর্মটি জমা দেবেন তখন আমরা নতুন তৈরি করা কাজটিকে 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 অ্যারেতে টাস্ক যোগ করি।

এটি কাজ করে তা নিশ্চিত করতে, আমাদের প্রথমে MatDialogModuleAppModule আমদানি করতে হবে:

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 এবং TaskDialogResultsrc/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

যখন আমরা "দুধ কিনুন" টাস্কটি টেনে আনতে শুরু করি, আমরা একই কাজের জন্য দুটি কার্ড দেখতে পাই - একটি আমরা টেনে নিয়ে যাচ্ছি এবং একটি সাঁতার কাটার মধ্যে৷ কৌণিক 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 প্যাকেজ ইনস্টল করে এবং আপনাকে কয়েকটি প্রশ্ন জিজ্ঞাসা করে। আপনার টার্মিনালে, আপনার এমন কিছু দেখতে হবে:

9ba88c0d52d18d0.png

ইতিমধ্যে, ইনস্টলেশন একটি ব্রাউজার উইন্ডো খোলে যাতে আপনি আপনার Firebase অ্যাকাউন্ট দিয়ে প্রমাণীকরণ করতে পারেন। অবশেষে, এটি আপনাকে একটি ফায়ারবেস প্রকল্প বেছে নিতে বলে এবং আপনার ডিস্কে কিছু ফাইল তৈরি করে।

এর পরে, আমাদের একটি ফায়ারস্টোর ডাটাবেস তৈরি করতে হবে! "ক্লাউড ফায়ারস্টোর" এর অধীনে "ডেটাবেস তৈরি করুন" এ ক্লিক করুন।

1e4a08b5a2462956.png

এর পরে, পরীক্ষা মোডে একটি ডাটাবেস তৈরি করুন:

ac1181b2c32049f9.png

অবশেষে, একটি অঞ্চল নির্বাচন করুন:

34bb94cc542a0597.png

আপনার পরিবেশে ফায়ারবেস কনফিগারেশন যোগ করাই এখন বাকি। আপনি Firebase কনসোলে আপনার প্রকল্পের কনফিগারেশন খুঁজে পেতে পারেন।

  • প্রজেক্ট ওভারভিউ এর পাশে গিয়ার আইকনে ক্লিক করুন।
  • প্রকল্প সেটিংস নির্বাচন করুন.

c8253a20031de8a9.png

"আপনার অ্যাপস" এর অধীনে একটি "ওয়েব অ্যাপ" নির্বাচন করুন:

428a1abcd0f90b23.png

এরপর, আপনার আবেদন নিবন্ধন করুন এবং নিশ্চিত করুন যে আপনি "Firebase হোস্টিং" সক্ষম করেছেন :

586e44cb27dd8f39.png

আপনি "অ্যাপ নিবন্ধন করুন" ক্লিক করার পরে, আপনি আপনার কনফিগারেশনটি 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. ফায়ারস্টোরে ডেটা সরানো

এখন যেহেতু আমরা 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.tsdrop পদ্ধতিটি এর সাথে প্রতিস্থাপন করুন:

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 নয়।

এখন আপনি যখন ইউজার ইন্টারফেসে একটি নতুন টাস্ক তৈরি করবেন এবং ফায়ারস্টোর খুলবেন, তখন আপনার এরকম কিছু দেখতে হবে:

dd7ee20c0a10ebe2.png

12. আশাবাদী আপডেট উন্নত করা

অ্যাপ্লিকেশনটিতে আমরা বর্তমানে আশাবাদী আপডেটগুলি সম্পাদন করছি। ফায়ারস্টোরে আমাদের সত্যের উৎস রয়েছে, কিন্তু একই সময়ে আমাদের কাছে কাজগুলির স্থানীয় অনুলিপি রয়েছে; যখন সংগ্রহের সাথে সম্পর্কিত যেকোন পর্যবেক্ষণযোগ্য পদার্থ নির্গত হয়, আমরা কাজগুলির একটি অ্যারে পাই। যখন কোনো ব্যবহারকারীর ক্রিয়া রাষ্ট্রকে পরিবর্তিত করে, আমরা প্রথমে স্থানীয় মান আপডেট করি এবং তারপর পরিবর্তনটিকে Firestore-এ প্রচার করি।

যখন আমরা একটি টাস্ককে একটি সাঁতারের পথ থেকে অন্য সাঁতারের পথে নিয়ে যাই, তখন আমরা TransferArrayItem আহ্বান করি transferArrayItem, যেটি অ্যারেগুলির স্থানীয় দৃষ্টান্তগুলির উপর কাজ করে যা প্রতিটি সাঁতারের পথে কাজগুলিকে উপস্থাপন করে৷ Firebase SDK এই অ্যারেগুলিকে অপরিবর্তনীয় হিসাবে বিবেচনা করে, যার অর্থ হল পরের বার Angular রান পরিবর্তন সনাক্তকরণের সময় আমরা তাদের নতুন উদাহরণ পাব, যা আমরা টাস্ক স্থানান্তর করার আগে পূর্ববর্তী অবস্থা রেন্ডার করবে।

একই সময়ে, আমরা একটি 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. একটি URL আউটপুট যাতে আপনি ফলাফলের পূর্বরূপ দেখতে পারেন।

14. অভিনন্দন

অভিনন্দন, আপনি সফলভাবে অ্যাঙ্গুলার এবং ফায়ারবেস সহ একটি কানবান বোর্ড তৈরি করেছেন!

আপনি তিনটি কলাম সহ একটি ব্যবহারকারী ইন্টারফেস তৈরি করেছেন যা বিভিন্ন কাজের স্থিতি উপস্থাপন করে। কৌণিক CDK ব্যবহার করে, আপনি কলাম জুড়ে টাস্ক এবং ড্রপ প্রয়োগ করেছেন। তারপর, কৌণিক উপাদান ব্যবহার করে, আপনি নতুন কাজ তৈরি করতে এবং বিদ্যমানগুলি সম্পাদনা করার জন্য একটি ফর্ম তৈরি করেছেন৷ এরপর, আপনি শিখেছেন কিভাবে @angular/fire ব্যবহার করতে হয় এবং সমস্ত অ্যাপ্লিকেশন স্টেট Firestore-এ সরানো হয়েছে। অবশেষে, আপনি আপনার অ্যাপ্লিকেশন ফায়ারবেস হোস্টিং-এ স্থাপন করেছেন।

এরপর কি?

মনে রাখবেন যে আমরা পরীক্ষা কনফিগারেশন ব্যবহার করে অ্যাপ্লিকেশন স্থাপন করেছি। প্রোডাকশনে আপনার অ্যাপ স্থাপন করার আগে আপনি সঠিক অনুমতি সেট আপ করেছেন তা নিশ্চিত করুন। আপনি এখানে কিভাবে এটি করতে শিখতে পারেন .

বর্তমানে, আমরা একটি নির্দিষ্ট সাঁতার কাটার পৃথক কাজের ক্রম সংরক্ষণ করি না। এটি বাস্তবায়ন করতে, আপনি টাস্ক ডকুমেন্টে একটি অর্ডার ক্ষেত্র ব্যবহার করতে পারেন এবং এটির উপর ভিত্তি করে সাজান।

উপরন্তু, আমরা শুধুমাত্র একজন একক ব্যবহারকারীর জন্য কানবান বোর্ড তৈরি করেছি, যার অর্থ হল যে অ্যাপটি খুলবে তাদের জন্য আমাদের কাছে একটি একক কানবান বোর্ড রয়েছে। আপনার অ্যাপের বিভিন্ন ব্যবহারকারীদের জন্য পৃথক বোর্ড প্রয়োগ করতে, আপনাকে আপনার ডাটাবেস গঠন পরিবর্তন করতে হবে। Firestore-এর সর্বোত্তম অনুশীলন সম্পর্কে এখানে জানুন।