Advanced JavaScript সিরিজের এগারো পর্বে আমরা শিখেছি Closure, Scope/Hoisting, this, Destructuring, Spread/Rest, Promise, OOP (Class, Inheritance), localStorage persistence, Error Handling, আর Module। এই শেষ পর্বে সবকিছু একসাথে মিলিয়ে বানাব একটা সম্পূর্ণ কাজ করা To-do List — Class-ভিত্তিক, কাস্টম এরর হ্যান্ডলিং সহ, আর রিফ্রেশের পরও ডেটা ধরে রাখবে।
প্রজেক্টে যা যা প্রয়োগ হচ্ছে
- Class (পর্ব ৭) —
TodoআরTodoListক্লাস - Closure (পর্ব ১) — প্রাইভেট স্টেট রক্ষণাবেক্ষণ
- Destructuring ও Spread (পর্ব ৪-৫) — ইমিউটেবল আপডেট
- localStorage persist (পর্ব ৯) —
fromJSONপ্যাটার্ন - Error Handling (পর্ব ১০) — কাস্টম
ValidationError - this (পর্ব ৩) — ইভেন্ট হ্যান্ডলারে সঠিক কনটেক্সট
ধাপ ১ — Todo ক্লাস
একটা একক টাস্কের জন্য ক্লাস, সাথে persist করার জন্য fromJSON static মেথড (পর্ব ৯-এর প্যাটার্ন):
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class Todo {
constructor(text, done = false, id = Date.now()) {
if (!text || text.trim() === "") {
throw new ValidationError("টাস্কের লেখা খালি রাখা যাবে না");
}
this.id = id;
this.text = text.trim();
this.done = done;
}
static fromJSON(data) {
return new Todo(data.text, data.done, data.id);
}
}
ধাপ ২ — TodoList ক্লাস (প্রাইভেট স্টেট + persist)
প্রাইভেট ফিল্ড (#, পর্ব ৭) দিয়ে স্টেট সুরক্ষিত রেখে, Spread (পর্ব ৫) দিয়ে ইমিউটেবল আপডেট করা হচ্ছে:
class TodoList {
#todos = [];
#storageKey;
constructor(storageKey) {
this.#storageKey = storageKey;
this.load();
}
add(text) {
const todo = new Todo(text); // ভুল ইনপুট হলে ValidationError থ্রো হবে
this.#todos = [...this.#todos, todo]; // ইমিউটেবল আপডেট
this.save();
return todo;
}
toggle(id) {
this.#todos = this.#todos.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo
);
this.save();
}
remove(id) {
this.#todos = this.#todos.filter((todo) => todo.id !== id);
this.save();
}
get all() {
return [...this.#todos]; // কপি রিটার্ন — বাইরে থেকে সরাসরি পাল্টানো যাবে না
}
get stats() {
const total = this.#todos.length;
const completed = this.#todos.filter((t) => t.done).length;
return { total, completed, remaining: total - completed };
}
save() {
localStorage.setItem(this.#storageKey, JSON.stringify(this.#todos));
}
load() {
try {
const raw = JSON.parse(localStorage.getItem(this.#storageKey)) || [];
this.#todos = raw.map((data) => Todo.fromJSON(data));
} catch (error) {
console.error("টাস্ক লোড করতে সমস্যা হয়েছে:", error);
this.#todos = [];
}
}
}
ধাপ ৩ — UI সংযুক্ত করা (this সচেতনতা)
পর্ব ৩-এ শেখা this সচেতনতা এখানে গুরুত্বপূর্ণ — arrow function ব্যবহার করে this ঠিক রাখা হচ্ছে:
const list = new TodoList("myTodos");
const form = document.getElementById("todoForm");
const input = document.getElementById("todoInput");
const listEl = document.getElementById("todoListEl");
const statsEl = document.getElementById("statsEl");
function render() {
listEl.innerHTML = "";
list.all.forEach((todo) => {
const li = document.createElement("li");
li.textContent = todo.text;
li.style.textDecoration = todo.done ? "line-through" : "none";
// arrow function — this এখানে দরকার নেই, কিন্তু লেক্সিক্যাল স্কোপ থেকে todo, list ঠিকই অ্যাক্সেস হচ্ছে
li.addEventListener("click", () => {
list.toggle(todo.id);
render();
});
listEl.appendChild(li);
});
const { total, completed, remaining } = list.stats; // Destructuring, পর্ব ৪
statsEl.textContent = `মোট: ${total}, সম্পন্ন: ${completed}, বাকি: ${remaining}`;
}
form.addEventListener("submit", (e) => {
e.preventDefault();
try {
list.add(input.value);
input.value = "";
render();
} catch (error) {
if (error instanceof ValidationError) {
alert(error.message); // কাস্টম এরর, পর্ব ১০
}
}
});
render();
লাইভ ডেমো — সম্পূর্ণ কাজ করা To-do List
নিচে সবকিছু একসাথে কাজ করছে — টাস্ক যোগ করো, ক্লিক করে সম্পন্ন করো, মুছে ফেলো, পেজ রিফ্রেশ করেও দেখো ডেটা থেকে যাচ্ছে।
চেষ্টা করে দেখো: খালি বক্সে সাবমিট করলে ValidationError ধরা পড়ে alert দেখাবে। টাস্কের লেখায় ক্লিক করলে সম্পন্ন/অসম্পন্ন টগল হবে। পেজ রিফ্রেশ করলেও সব থেকে যাবে।
কোড কীভাবে সিরিজের সব শেখা মেলাচ্ছে — সংক্ষেপে
| প্রজেক্টের অংশ | কোন পর্বের কনসেপ্ট |
|---|---|
#todos প্রাইভেট ফিল্ড |
পর্ব ৭ (Class) |
ValidationError extends Error |
পর্ব ৮ (Inheritance), পর্ব ১০ (Error Handling) |
{ ...todo, done: !todo.done } |
পর্ব ৫ (Spread) |
const { total, completed } = list.stats |
পর্ব ৪ (Destructuring) |
Todo.fromJSON(data) |
পর্ব ৯ (localStorage persist) |
| Arrow function ইভেন্ট হ্যান্ডলার | পর্ব ৩ (this) |
পরবর্তী উন্নতির আইডিয়া
Exercise: প্রজেক্টটা আরও ভালো করার জন্য নিজে চেষ্টা করো —
- টাস্ক এডিট করার ফিচার যোগ করো (ডাবল-ক্লিক করলে ইনপুটে বদলে যাবে)।
- ফিল্টার বাটন যোগ করো — "সব", "সম্পন্ন", "বাকি" অনুযায়ী তালিকা দেখানো।
- একটা
clearCompleted()মেথডTodoList-এ যোগ করো, যেটা সব সম্পন্ন টাস্ক একসাথে মুছে দেবে। - প্রজেক্টটা পর্ব ১১-এর টেকনিক দিয়ে
models/todo.js,models/todo-list.js,main.js-এ আলাদা ফাইলে ভাগ করো।
সিরিজ শেষ!
Advanced JavaScript সিরিজের বারো পর্বে আমরা Closure থেকে শুরু করে Scope, this, Destructuring, Spread/Rest, Promise, OOP, Error Handling, Module পর্যন্ত গিয়েছি, আর আজ সেগুলো একসাথে মিলিয়ে একটা সত্যিকারের কাজ করা প্রজেক্ট বানিয়েছি।