# ব্যাংকিং অ্যাপ তৈরি করুন পার্ট ১: HTML টেমপ্লেট এবং ওয়েব অ্যাপে রাউটস
## প্রি-লেকচার কুইজ
[প্রি-লেকচার কুইজ](https://ff-quizzes.netlify.app/web/quiz/41)
### ভূমিকা
ব্রাউজারে জাভাস্ক্রিপ্ট আসার পর থেকে ওয়েবসাইটগুলো আগের চেয়ে অনেক বেশি ইন্টারঅ্যাকটিভ এবং জটিল হয়ে উঠেছে। এখন ওয়েব প্রযুক্তি ব্যবহার করে সম্পূর্ণ কার্যকরী অ্যাপ্লিকেশন তৈরি করা হয় যা সরাসরি ব্রাউজারে চলে, যাকে আমরা [ওয়েব অ্যাপ্লিকেশন](https://en.wikipedia.org/wiki/Web_application) বলি। যেহেতু ওয়েব অ্যাপগুলো খুবই ইন্টারঅ্যাকটিভ, ব্যবহারকারীরা প্রতিবার কোনো অ্যাকশন সম্পন্ন করার সময় পুরো পেজ রিলোডের জন্য অপেক্ষা করতে চান না। এজন্য জাভাস্ক্রিপ্ট ব্যবহার করে HTML সরাসরি DOM এর মাধ্যমে আপডেট করা হয়, যা ব্যবহারকারীদের জন্য আরও মসৃণ অভিজ্ঞতা প্রদান করে।
এই পাঠে, আমরা একটি ব্যাংক ওয়েব অ্যাপ তৈরি করার ভিত্তি স্থাপন করব, যেখানে HTML টেমপ্লেট ব্যবহার করে একাধিক স্ক্রিন তৈরি করা হবে যা পুরো HTML পেজ রিলোড না করেই প্রদর্শিত এবং আপডেট করা যাবে।
### পূর্বশর্ত
এই পাঠে আমরা যে ওয়েব অ্যাপ তৈরি করব তা পরীক্ষা করার জন্য আপনার একটি লোকাল ওয়েব সার্ভার প্রয়োজন। যদি আপনার কাছে না থাকে, তাহলে [Node.js](https://nodejs.org) ইনস্টল করুন এবং আপনার প্রজেক্ট ফোল্ডার থেকে `npx lite-server` কমান্ড ব্যবহার করুন। এটি একটি লোকাল ওয়েব সার্ভার তৈরি করবে এবং আপনার অ্যাপটি ব্রাউজারে খুলবে।
### প্রস্তুতি
আপনার কম্পিউটারে `bank` নামে একটি ফোল্ডার তৈরি করুন এবং এর মধ্যে `index.html` নামে একটি ফাইল রাখুন। আমরা এই HTML [বয়লারপ্লেট](https://en.wikipedia.org/wiki/Boilerplate_code) থেকে শুরু করব:
```html
Bank App
```
---
## HTML টেমপ্লেট
যদি আপনি একটি ওয়েব পেজের জন্য একাধিক স্ক্রিন তৈরি করতে চান, তাহলে একটি সমাধান হতে পারে প্রতিটি স্ক্রিনের জন্য আলাদা HTML ফাইল তৈরি করা। তবে, এই সমাধান কিছু অসুবিধা নিয়ে আসে:
- স্ক্রিন পরিবর্তনের সময় পুরো HTML রিলোড করতে হয়, যা ধীর হতে পারে।
- বিভিন্ন স্ক্রিনের মধ্যে ডেটা শেয়ার করা কঠিন।
আরেকটি পদ্ধতি হলো শুধুমাত্র একটি HTML ফাইল রাখা এবং `` এলিমেন্ট ব্যবহার করে একাধিক [HTML টেমপ্লেট](https://developer.mozilla.org/docs/Web/HTML/Element/template) সংজ্ঞায়িত করা। একটি টেমপ্লেট হলো পুনরায় ব্যবহারযোগ্য HTML ব্লক যা ব্রাউজার দ্বারা প্রদর্শিত হয় না এবং এটি রানটাইমে জাভাস্ক্রিপ্ট ব্যবহার করে ইনস্ট্যানশিয়েট করতে হয়।
### কাজ
আমরা একটি ব্যাংক অ্যাপ তৈরি করব যেখানে দুটি স্ক্রিন থাকবে: লগইন পেজ এবং ড্যাশবোর্ড। প্রথমে, HTML বডিতে একটি প্লেসহোল্ডার এলিমেন্ট যোগ করুন যা আমাদের অ্যাপের বিভিন্ন স্ক্রিন ইনস্ট্যানশিয়েট করতে ব্যবহার করা হবে:
```html
Loading...
```
আমরা এটিকে একটি `id` দিয়েছি যাতে এটি পরে জাভাস্ক্রিপ্ট দিয়ে সহজে খুঁজে পাওয়া যায়।
> টিপ: যেহেতু এই এলিমেন্টের কন্টেন্ট প্রতিস্থাপিত হবে, আমরা এখানে একটি লোডিং মেসেজ বা ইন্ডিকেটর রাখতে পারি যা অ্যাপ লোড হওয়ার সময় দেখানো হবে।
এরপর, লগইন পেজের জন্য HTML টেমপ্লেট যোগ করুন। আপাতত আমরা এখানে একটি শিরোনাম এবং একটি সেকশন রাখব যেখানে একটি লিঙ্ক থাকবে যা নেভিগেশনের জন্য ব্যবহার করা হবে।
```html
Bank App
```
এরপর আমরা ড্যাশবোর্ড পেজের জন্য আরেকটি HTML টেমপ্লেট যোগ করব। এই পেজে বিভিন্ন সেকশন থাকবে:
- একটি হেডার যেখানে শিরোনাম এবং লগআউট লিঙ্ক থাকবে
- ব্যাংক অ্যাকাউন্টের বর্তমান ব্যালেন্স
- একটি টেবিলে প্রদর্শিত ট্রানজ্যাকশনের তালিকা
```html
```
> টিপ: HTML টেমপ্লেট তৈরি করার সময়, যদি আপনি দেখতে চান এটি কেমন দেখাবে, তাহলে `` এবং `` লাইনগুলোকে `` দিয়ে কমেন্ট আউট করতে পারেন।
✅ আপনি কেন মনে করেন আমরা টেমপ্লেটগুলোর উপর `id` অ্যাট্রিবিউট ব্যবহার করি? আমরা কি এর পরিবর্তে ক্লাস ব্যবহার করতে পারতাম?
## জাভাস্ক্রিপ্ট দিয়ে টেমপ্লেট প্রদর্শন
যদি আপনি আপনার বর্তমান HTML ফাইলটি ব্রাউজারে চেষ্টা করেন, তাহলে দেখবেন এটি `Loading...` দেখিয়ে আটকে আছে। এর কারণ হলো আমাদের কিছু জাভাস্ক্রিপ্ট কোড যোগ করতে হবে যা HTML টেমপ্লেট ইনস্ট্যানশিয়েট এবং প্রদর্শন করবে।
টেমপ্লেট ইনস্ট্যানশিয়েট সাধারণত তিনটি ধাপে করা হয়:
1. DOM-এ টেমপ্লেট এলিমেন্টটি খুঁজে বের করা, যেমন [`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById) ব্যবহার করে।
2. টেমপ্লেট এলিমেন্টটি ক্লোন করা, [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode) ব্যবহার করে।
3. এটি দৃশ্যমান এলিমেন্টের নিচে DOM-এ সংযুক্ত করা, যেমন [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild) ব্যবহার করে।
✅ আমরা কেন টেমপ্লেট ক্লোন করার প্রয়োজন হয় DOM-এ সংযুক্ত করার আগে? যদি আমরা এই ধাপটি বাদ দেই তাহলে কী হতে পারে?
### কাজ
আপনার প্রজেক্ট ফোল্ডারে `app.js` নামে একটি নতুন ফাইল তৈরি করুন এবং HTML এর `` সেকশনে সেই ফাইলটি ইমপোর্ট করুন:
```html
```
এখন `app.js`-এ আমরা একটি নতুন ফাংশন `updateRoute` তৈরি করব:
```js
function updateRoute(templateId) {
const template = document.getElementById(templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
এখানে আমরা ঠিক তিনটি ধাপ অনুসরণ করেছি যা আগে উল্লেখ করা হয়েছে। আমরা `templateId` এর টেমপ্লেট ইনস্ট্যানশিয়েট করি এবং এর ক্লোন করা কন্টেন্ট আমাদের অ্যাপের প্লেসহোল্ডারে রাখি। লক্ষ্য করুন যে আমরা `cloneNode(true)` ব্যবহার করেছি পুরো সাবট্রি কপি করার জন্য।
এখন এই ফাংশনটি একটি টেমপ্লেট দিয়ে কল করুন এবং ফলাফল দেখুন।
```js
updateRoute('login');
```
✅ এই কোডের উদ্দেশ্য কী `app.innerHTML = '';`? এটি ছাড়া কী ঘটবে?
## রাউট তৈরি করা
যখন একটি ওয়েব অ্যাপের কথা বলা হয়, তখন আমরা *Routing* বলতে বুঝি **URLs**-কে নির্দিষ্ট স্ক্রিনের সাথে ম্যাপ করা যা প্রদর্শিত হওয়া উচিত। একাধিক HTML ফাইল সহ একটি ওয়েবসাইটে, এটি স্বয়ংক্রিয়ভাবে করা হয় কারণ ফাইল পাথগুলো URL-এ প্রতিফলিত হয়। উদাহরণস্বরূপ, আপনার প্রজেক্ট ফোল্ডারে এই ফাইলগুলো থাকলে:
```
mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html
```
যদি আপনি `mywebsite` কে রুট হিসেবে ব্যবহার করে একটি ওয়েব সার্ভার তৈরি করেন, তাহলে URL ম্যাপিং হবে:
```
https://site.com --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
তবে, আমাদের ওয়েব অ্যাপের জন্য আমরা একটি HTML ফাইল ব্যবহার করছি যেখানে সব স্ক্রিন রয়েছে, তাই এই ডিফল্ট আচরণ আমাদের সাহায্য করবে না। আমাদের এই ম্যাপটি ম্যানুয়ালি তৈরি করতে হবে এবং জাভাস্ক্রিপ্ট ব্যবহার করে প্রদর্শিত টেমপ্লেট আপডেট করতে হবে।
### কাজ
আমরা একটি সাধারণ অবজেক্ট ব্যবহার করব একটি [ম্যাপ](https://en.wikipedia.org/wiki/Associative_array) বাস্তবায়ন করতে যা URL পাথ এবং আমাদের টেমপ্লেটগুলোর মধ্যে সংযোগ স্থাপন করবে। এই অবজেক্টটি `app.js` ফাইলের শীর্ষে যোগ করুন।
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard' },
};
```
এখন `updateRoute` ফাংশনটি একটু পরিবর্তন করি। সরাসরি `templateId` আর্গুমেন্ট হিসেবে পাস করার পরিবর্তে, আমরা প্রথমে বর্তমান URL দেখব এবং তারপর আমাদের ম্যাপ ব্যবহার করে সংশ্লিষ্ট টেমপ্লেট ID মানটি পাব। আমরা [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) ব্যবহার করতে পারি URL থেকে শুধুমাত্র পাথ সেকশনটি পেতে।
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
এখানে আমরা আমাদের ঘোষিত রাউটগুলোকে সংশ্লিষ্ট টেমপ্লেটের সাথে ম্যাপ করেছি। এটি সঠিকভাবে কাজ করছে কিনা পরীক্ষা করতে আপনার ব্রাউজারে URL ম্যানুয়ালি পরিবর্তন করুন।
✅ যদি আপনি URL-এ একটি অজানা পাথ প্রবেশ করেন তাহলে কী ঘটে? আমরা কীভাবে এটি সমাধান করতে পারি?
## নেভিগেশন যোগ করা
আমাদের অ্যাপের পরবর্তী ধাপ হলো পেজগুলোর মধ্যে নেভিগেট করার সুবিধা যোগ করা, যাতে URL ম্যানুয়ালি পরিবর্তন করতে না হয়। এটি দুটি জিনিসকে অন্তর্ভুক্ত করে:
1. বর্তমান URL আপডেট করা
2. নতুন URL এর উপর ভিত্তি করে প্রদর্শিত টেমপ্লেট আপডেট করা
আমরা ইতিমধ্যে `updateRoute` ফাংশন দিয়ে দ্বিতীয় অংশটি সম্পন্ন করেছি, তাই আমাদের বর্তমান URL আপডেট করার উপায় বের করতে হবে।
আমাদের জাভাস্ক্রিপ্ট ব্যবহার করতে হবে এবং বিশেষভাবে [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState) ব্যবহার করতে হবে যা URL আপডেট করতে এবং ব্রাউজিং হিস্টোরিতে একটি নতুন এন্ট্রি তৈরি করতে সাহায্য করে, HTML রিলোড না করেই।
> নোট: HTML অ্যাঙ্কর এলিমেন্ট [``](https://developer.mozilla.org/docs/Web/HTML/Element/a) নিজে থেকেই বিভিন্ন URL-এ হাইপারলিঙ্ক তৈরি করতে ব্যবহার করা যেতে পারে, তবে এটি ডিফল্টভাবে HTML রিলোড করবে। কাস্টম জাভাস্ক্রিপ্ট দিয়ে রাউটিং পরিচালনা করার সময় এই আচরণটি প্রতিরোধ করা প্রয়োজন, যা `preventDefault()` ফাংশন ব্যবহার করে ক্লিক ইভেন্টে করা যায়।
### কাজ
আমাদের অ্যাপে নেভিগেট করার জন্য একটি নতুন ফাংশন তৈরি করি:
```js
function navigate(path) {
window.history.pushState({}, path, path);
updateRoute();
}
```
এই মেথড প্রথমে প্রদত্ত পাথের উপর ভিত্তি করে বর্তমান URL আপডেট করে, তারপর টেমপ্লেট আপডেট করে। `window.location.origin` প্রপার্টি URL রুট প্রদান করে, যা আমাদের একটি প্রদত্ত পাথ থেকে সম্পূর্ণ URL পুনর্গঠন করতে সাহায্য করে।
এখন আমাদের এই ফাংশনটি আছে, আমরা যদি কোনো পাথ কোনো সংজ্ঞায়িত রাউটের সাথে মিলে না যায় তাহলে কী করব সেই সমস্যাটি সমাধান করতে পারি। আমরা `updateRoute` ফাংশনটি পরিবর্তন করব এবং একটি বিদ্যমান রাউটে ফিরে যাওয়ার ব্যবস্থা করব যদি কোনো ম্যাচ না পাওয়া যায়।
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
...
```
যদি কোনো রাউট খুঁজে পাওয়া না যায়, তাহলে আমরা এখন `login` পেজে রিডাইরেক্ট করব।
এখন একটি ফাংশন তৈরি করি যা একটি লিঙ্ক ক্লিক করার সময় URL পায় এবং ব্রাউজারের ডিফল্ট লিঙ্ক আচরণ প্রতিরোধ করে:
```js
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
```
আমাদের নেভিগেশন সিস্টেম সম্পূর্ণ করতে আমাদের HTML-এ *Login* এবং *Logout* লিঙ্কগুলোর জন্য বাইন্ডিং যোগ করতে হবে।
```html
Login
...
Logout
```
উপরের `event` অবজেক্টটি `click` ইভেন্ট ক্যাপচার করে এবং আমাদের `onLinkClick` ফাংশনে পাস করে।
[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) অ্যাট্রিবিউট ব্যবহার করে `click` ইভেন্টকে জাভাস্ক্রিপ্ট কোডের সাথে বাইন্ড করুন, এখানে `navigate()` ফাংশন কল।
এই লিঙ্কগুলোতে ক্লিক করে দেখুন, এখন আপনি আপনার অ্যাপের বিভিন্ন স্ক্রিনে নেভিগেট করতে সক্ষম হওয়া উচিত।
✅ `history.pushState` মেথড HTML5 স্ট্যান্ডার্ডের অংশ এবং [সব আধুনিক ব্রাউজারে](https://caniuse.com/?search=pushState) ইমপ্লিমেন্ট করা হয়েছে। যদি আপনি পুরনো ব্রাউজারের জন্য একটি ওয়েব অ্যাপ তৈরি করেন, তাহলে এই API-এর পরিবর্তে একটি কৌশল ব্যবহার করতে পারেন: একটি [হ্যাশ (`#`)](https://en.wikipedia.org/wiki/URI_fragment) ব্যবহার করে পাথের আগে আপনি এমন রাউটিং বাস্তবায়ন করতে পারেন যা নিয়মিত অ্যাঙ্কর নেভিগেশনের সাথে কাজ করে এবং পেজ রিলোড করে না, কারণ এর উদ্দেশ্য ছিল একটি পেজের মধ্যে অভ্যন্তরীণ লিঙ্ক তৈরি করা।
## ব্রাউজারের ব্যাক এবং ফরওয়ার্ড বাটন পরিচালনা করা
`history.pushState` ব্যবহার করে ব্রাউজারের নেভিগেশন হিস্টোরিতে নতুন এন্ট্রি তৈরি করা হয়। আপনি যদি ব্রাউজারের *ব্যাক বাটন* ধরে রাখেন, তাহলে এটি কিছুটা এরকম দেখাবে:

আপনি যদি কয়েকবার ব্যাক বাটনে ক্লিক করেন, তাহলে দেখবেন বর্তমান URL পরিবর্তিত হচ্ছে এবং হিস্টোরি আপডেট হচ্ছে, কিন্তু একই টেমপ্লেট প্রদর্শিত হচ্ছে।
এর কারণ হলো অ্যাপ্লিকেশন জানে না যে আমাদের প্রতিবার হিস্টোরি পরিবর্তন হলে `updateRoute()` কল করতে হবে। যদি আপনি [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState) ডকুমেন্টেশন দেখেন, তাহলে দেখতে পাবেন যে যদি স্টেট পরিবর্তন হয় - অর্থাৎ আমরা একটি ভিন্ন URL-এ চলে যাই - তাহলে [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event) ইভেন্ট ট্রিগার হয়। আমরা এটি ব্যবহার করে এই সমস্যাটি ঠিক করব।
### কাজ
যখন ব্রাউজারের হিস্টোরি পরিবর্তন হয় তখন প্রদর্শিত টেমপ্লেট আপডেট নিশ্চিত করতে, আমরা একটি নতুন ফাংশন সংযুক্ত করব যা `updateRoute()` কল করে। আমরা এটি `app.js` ফাইলের নিচে করব:
```js
window.onpopstate = () => updateRoute();
updateRoute();
```
> নোট: আমরা এখানে আমাদের `popstate` ইভেন্ট হ্যান্ডলার ঘোষণা করার জন্য [arrow function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions) ব্যবহার করেছি সংক্ষিপ্ততার জন্য, তবে একটি সাধারণ ফাংশনও একইভাবে কাজ করবে।
এখানে arrow functions নিয়ে একটি রিফ্রেশার ভিডিও:
[](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions")
> 🎥 উপরের ছবিতে ক্লিক করুন arrow functions সম্পর্কে একটি ভিডিও দেখতে।
এখন ব্রাউজারের ব্যাক এবং ফরওয়ার্ড বাটন ব্যবহার করে দেখুন, এবং নিশ্চিত করুন যে প্রদর্শিত রাউট এবার সঠিকভাবে আপডেট হচ্ছে।
---
## 🚀 চ্যালেঞ্জ
এই অ্যাপের ক্রেডিট দেখানোর জন্য একটি তৃতীয় পেজের জন্য একটি নতুন টেমপ্লেট এবং রাউট যোগ করুন।
## পোস্ট-লেকচার কুইজ
[পোস্ট-লেকচার কুইজ](https://ff-quizzes.netlify.app/web/quiz/42)
## রিভিউ এবং সেলফ স্টাডি
রাউটিং ওয়েব ডেভেলপমেন্টের একটি আশ্চর্যজনকভাবে জটিল অংশ, বিশেষ করে যখন ওয়েব পেজ রিফ্রেশ আচরণ থেকে সিঙ্গেল পেজ অ্যাপ্লিকেশন পেজ রিফ্রেশে চলে যায়। [Azure Static Web App সার্ভিস](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon) কীভাবে রাউটিং পরিচালনা করে তা সম্পর্কে একটু পড়ুন। আপনি কি ব্যাখ্যা করতে পারেন কেন এই ডকুমেন্টে বর্ণিত কিছু সিদ্ধান্ত প্রয়োজনীয়?
## অ্যাসাইনমেন্ট
[রাউটিং উন্নত করুন](assignment.md)
---
**অস্বীকৃতি**:
এই নথিটি AI অনুবাদ পরিষেবা [Co-op Translator](https://github.com/Azure/co-op-translator) ব্যবহার করে অনুবাদ করা হয়েছে। আমরা যথাসম্ভব সঠিক অনুবাদের চেষ্টা করি, তবে অনুগ্রহ করে মনে রাখবেন যে স্বয়ংক্রিয় অনুবাদে ত্রুটি বা অসঙ্গতি থাকতে পারে। নথিটির মূল ভাষায় লেখা সংস্করণটিকেই প্রামাণিক উৎস হিসেবে বিবেচনা করা উচিত। গুরুত্বপূর্ণ তথ্যের জন্য, পেশাদার মানব অনুবাদ ব্যবহার করার পরামর্শ দেওয়া হচ্ছে। এই অনুবাদ ব্যবহারের ফলে সৃষ্ট কোনো ভুল বোঝাবুঝি বা ভুল ব্যাখ্যার জন্য আমরা দায়ী নই।