:dollar: Build a Bank
In this project, you'll learn how to build a fictional bank. These lessons include instructions on how to layout a web app and provide routes, build forms, manage state, and fetch data from an API from which you can fetch the bank's data. ## Lessons 1. HTML Templates and Routes in a Web App 2. Build a Login and Registration Form 3. Methods of Fetching and Using Data 4. Concepts of State Management ### Credits These lessons were written with :hearts: by Yohan Lasorsa. If you're interested to learn how to build the server API used in these lessons, you can follow this series of videos (in particular videos 17 through 21). You can also take a look at this interactive Learn tutorial.
Build a Banking App Part 1: HTML Templates and Routes in a Web App
When Apollo 11's guidance computer navigated to the moon in 1969, it had to switch between different programs without restarting the entire system. Modern web applications work similarly β they change what you see without reloading everything from scratch. This creates the smooth, responsive experience users expect today. Unlike traditional websites that reload entire pages for every interaction, modern web apps update only the parts that need changing. This approach, much like how mission control switches between different displays while maintaining constant communication, creates that fluid experience we've come to expect. Here's what makes the difference so dramatic: Understanding the evolution: - Traditional apps require server requests for every navigation action - Modern SPAs load once and update content dynamically using JavaScript - User expectations now favor instant, seamless interactions - Performance benefits include reduced bandwidth and faster responses In this lesson, we'll build a banking app with multiple screens that flow together seamlessly. Like how scientists use modular instruments that can be reconfigured for different experiments, we'll use HTML templates as reusable components that can be displayed as needed. You'll work with HTML templates (reusable blueprints for different screens), JavaScript routing (the system that switches between screens), and the browser's history API (which keeps the back button working as expected). These are the same fundamental techniques used by frameworks like React, Vue, and Angular. By the end, you'll have a working banking app that demonstrates professional single-page application principles. ## Pre-Lecture Quiz Pre-lecture quiz ### What You'll Need We'll need a local web server to test our banking app β don't worry, it's easier than it sounds! If you don't already have one set up, just install Node.js and run npx lite-server from your project folder. This handy command spins up a local server and automatically opens your app in the browser. ### Preparation On your computer, create a folder named bank with a file named index.html inside it. We'll start from this HTML boilerplate: Here's what this boilerplate provides: - Establishes the HTML5 document structure with proper DOCTYPE declaration - Configures character encoding as UTF-8 for international text support - Enables responsive design with the viewport meta tag for mobile compatibility - Sets a descriptive title that appears in the browser tab - Creates a clean body section where we'll build our application --- ## HTML Templates Templates solve a fundamental problem in web development. When Gutenberg invented movable type printing in the 1440s, he realized that instead of carving entire pages, he could create reusable letter blocks and arrange them as needed. HTML templates work on the same principle β instead of creating separate HTML files for each screen, you define reusable structures that can be displayed when needed. Think of templates as blueprints for different parts of your app. Just as an architect creates one blueprint and uses it multiple times rather than redrawing identical rooms, we create templates once and instantiate them as needed. The browser keeps these templates hidden until JavaScript activates them. If you want to create multiple screens for a web page, one solution would be to create one HTML file for every screen you want to display. However, this solution comes with some inconvenience: - You have to reload the entire HTML when switching screen, which can be slow. - It's difficult to share data between the different screens. Another approach is to have only one HTML file, and define multiple HTML templates using the <template> element. A template is a reusable HTML block that is not displayed by the browser, and needs to be instantiated at runtime using JavaScript. ### Let's Build It We're going to create a bank app with two main screens: a login page and a dashboard. First, let's add a placeholder element to our HTML body β this is where all our different screens will appear: Understanding this placeholder: - Creates a container with the ID "app" where all screens will be displayed - Shows a loading message until the JavaScript initializes the first screen - Provides a single mounting point for our dynamic content - Enables easy targeting from JavaScript using document.getElementById() Next, let's add below the HTML template for the login page. For now we'll only put in there a title and a section containing a link that we'll use to perform the navigation. Breaking down this login template: - Defines a template with the unique identifier "login" for JavaScript targeting - Includes a main heading that establishes the app's branding - Contains a semantic <section> element to group related content - Provides a navigation link that will route users to the dashboard Then we'll add another HTML template for the dashboard page. This page will contain different sections: - A header with a title and a logout link - The current balance of the bank account - A list of transactions, displayed in a table Let's understand each part of this dashboard: - Structures the page with a semantic <header> element containing navigation - Displays the app title consistently across screens for branding - Provides a logout link that routes back to the login screen - Shows the current account balance in a dedicated section - Organizes transaction data using a properly structured HTML table - Defines table headers for Date, Object, and Amount columns - Leaves the table body empty for dynamic content injection later ### π Pedagogical Check-in Template System Understanding: Before implementing JavaScript, ensure you understand: - β How templates differ from regular HTML elements - β Why templates remain hidden until activated by JavaScript - β The importance of semantic HTML structure in templates - β How templates enable reusable UI components Quick Self-Test: What happens if you remove the <template> tags from around your HTML? Answer: The content becomes visible immediately and loses its template functionality Architecture Benefits: Templates provide: - Reusability: One definition, multiple instances - Performance: No redundant HTML parsing - Maintainability: Centralized UI structure - Flexibility: Dynamic content switching β Why do you think we use id attributes on the templates? Could we use something else like classes? ## Bringing Templates to Life with JavaScript Now we need to make our templates functional. Like how a 3D printer takes a digital blueprint and creates a physical object, JavaScript takes our hidden templates and creates visible, interactive elements that users can see and use. The process follows three consistent steps that form the foundation of modern web development. Once you understand this pattern, you'll recognize it across many frameworks and libraries. If you try your current HTML file in a browser, you'll see that it gets stuck displaying Loading.... That's because we need to add some JavaScript code to instantiate and display the HTML templates. Instantiating a template is usually done in 3 steps: 1. Retrieve the template element in the DOM, for example using document.getElementById. 2. Clone the template element, using cloneNode. 3. Attach it to the DOM under a visible element, for example using appendChild. Visual breakdown of the process: - Step 1 locates the hidden template in the DOM structure - Step 2 creates a working copy that can be safely modified - Step 3 inserts the copy into the visible page area - Result is a functional screen that users can interact with β Why do we need to clone the template before attaching it to the DOM? What do you think would happen if we skipped this step? ### Task Create a new file named app.js in your project folder and import that file in the <head> section of your HTML: Understanding this script import: - Links the JavaScript file to our HTML document - Uses the defer attribute to ensure the script runs after HTML parsing completes - Enables access to all DOM elements since they're fully loaded before script execution - Follows modern best practices for script loading and performance Now in app.js, we'll create a new function updateRoute: Step by step, here's what's happening: - Locates the template element using its unique ID - Creates a deep copy of the template's content using cloneNode(true) - Finds the app container where the content will be displayed - Clears any existing content from the app container - Inserts the cloned template content into the visible DOM Now call this function with one of the template and look at the result. What this function call accomplishes: - Activates the login template by passing its ID as a parameter - Demonstrates how to programmatically switch between different app screens - Shows the login screen in place of the "Loading..." message β What's the purpose of this code app.innerHTML = '';? What happens without it? ## Creating Routes Routing is essentially about connecting URLs to the right content. Consider how early telephone operators used switchboards to connect calls β they would take an incoming request and route it to the correct destination. Web routing works similarly, taking a URL request and determining which content to display. Traditionally, web servers handled this by serving different HTML files for different URLs. Since we're building a single-page app, we need to handle this routing ourselves with JavaScript. This approach gives us more control over the user experience and performance. Understanding the routing flow: - URL changes trigger a lookup in our routes configuration - Valid routes map to specific template IDs for rendering - Invalid routes trigger fallback behavior to prevent broken states - Template rendering follows the three-step process we learned earlier When talking about a web app, we call Routing the intent to map URLs to specific screens that should be displayed. On a website with multiple HTML files, this is done automatically as the file paths are reflected on the URL. For example, with these files in your project folder: If you create a web server with mywebsite as the root, the URL mapping will be: However, for our web app we are using a single HTML file containing all the screens so this default behavior won't help us. We have to create this map manually and perform update the displayed template using JavaScript. ### Task We'll use a simple object to implement a map between URL paths and our templates. Add this object at the top of your app.js file. Understanding this routes configuration: - Defines a mapping between URL paths and template identifiers - Uses object syntax where keys are URL paths and values contain template information - Enables easy lookup of which template to display for any given URL - Provides a scalable structure for adding new routes in the future Now let's modify a bit the updateRoute function. Instead of passing directly the templateId as an argument, we want to retrieve it by first looking at the current URL, and then use our map to get the corresponding template ID value. We can use window.location.pathname to get only the path section from the URL. Breaking down what happens here: - Extracts the current path from the browser's URL using window.location.pathname - Looks up the corresponding route configuration in our routes object - Retrieves the template ID from the route configuration - Follows the same template rendering process as before - Creates a dynamic system that responds to URL changes Here we mapped the routes we declared to the corresponding template. You can try it that it works correctly by changing the URL manually in your browser. β What happens if you enter an unknown path in the URL? How could we solve this? ## Adding Navigation With routing established, users need a way to navigate through the app. Traditional websites reload entire pages when clicking links, but we want to update both the URL and content without page refreshes. This creates a smoother experience similar to how desktop applications switch between different views. We need to coordinate two things: updating the browser's URL so users can bookmark pages and share links, and displaying the appropriate content. When implemented correctly, this creates the seamless navigation users expect from modern applications. ### π Pedagogical Check-in Single-Page Application Architecture: Verify your understanding of the complete system: - β How does client-side routing differ from traditional server-side routing? - β Why is the History API essential for proper SPA navigation? - β How do templates enable dynamic content without page reloads? - β What role does event handling play in intercepting navigation? System Integration: Your SPA demonstrates: - Template Management: Reusable UI components with dynamic content - Client-side Routing: URL management without server requests - Event-driven Architecture: Responsive navigation and user interactions - Browser Integration: Proper history and back/forward button support - Performance Optimization: Fast transitions and reduced server load Professional Patterns: You've implemented: - Model-View Separation: Templates separate from application logic - State Management: URL state synchronized with displayed content - Progressive Enhancement: JavaScript enhances basic HTML functionality - User Experience: Smooth, app-like navigation without page refreshes The next step for our app is to add the possibility to navigate between pages without having to change the URL manually. This implies two things: 1. Updating the current URL 2. Updating the displayed template based on the new URL We already took care of the second part with the updateRoute function, so we have to figure out how to update the current URL. We'll have to use JavaScript and more specifically the history.pushState that allows to update the URL and create a new entry in the browsing history, without reloading the HTML. ### Task Let's create a new function we can use to navigate in our app: Understanding this navigation function: - Updates the browser's URL to the new path using history.pushState - Adds a new entry to the browser's history stack for proper back/forward button support - Triggers the updateRoute() function to display the corresponding template - Maintains the single-page app experience without page reloads This method first updates the current URL based on the path given, then updates the template. The property window.location.origin returns the URL root, allowing us to reconstruct a complete URL from a given path. Now that we have this function, we can take care of the problem we have if a path does not match any defined route. We'll modify the updateRoute function by adding a fallback to one of the existing route if we can't find a match. Key points to remember: - Checks if a route exists for the current path - Redirects to the login page when an invalid route is accessed - Provides a fallback mechanism that prevents broken navigation - Ensures users always see a valid screen, even with incorrect URLs If a route cannot be found, we'll now redirect to the login page. Now let's create a function to get the URL when a link is clicked, and to prevent the browser's default link behavior: Breaking down this click handler: - Prevents the browser's default link behavior using preventDefault() - Extracts the destination URL from the clicked link element - Calls our custom navigate function instead of reloading the page - Maintains the smooth single-page application experience What this onclick binding accomplishes: - Connects each link to our custom navigation system - Passes the click event to our onLinkClick function for processing - Enables smooth navigation without page reloads - Maintains proper URL structure that users can bookmark or share The onclick attribute bind the click event to JavaScript code, here the call to the navigate() function. Try clicking on these links, you should be now able to navigate between the different screens of your app. β The history.pushState method is part of the HTML5 standard and implemented in all modern browsers. If you're building a web app for older browsers, there's a trick you can use in place of this API: using a hash (#) before the path you can implement routing that works with regular anchor navigation and does not reload the page, as it's purpose was to create internal links within a page. ## Making the Back and Forward Buttons Work The back and forward buttons are fundamental to web browsing, much like how NASA mission controllers can review previous system states during space missions. Users expect these buttons to work, and when they don't, it breaks the expected browsing experience. Our single-page app needs additional configuration to support this. The browser maintains a history stack (which we've been adding to with history.pushState), but when users navigate through this history, our app needs to respond by updating the displayed content accordingly. Key interaction points: - User actions trigger navigation through clicks or browser buttons - App intercepts link clicks to prevent page reloads - History API manages URL changes and browser history stack - Templates provide the content structure for each screen - Event listeners ensure the app responds to all navigation types Using the history.pushState creates new entries in the browser's navigation history. You can check that by holding the back button of your browser, it should display something like this: If you try clicking on the back button a few times, you'll see that the current URL changes and the history is updated, but the same template keeps being displayed. That's because the application does not know that we need to call updateRoute() every time the history changes. If you take a look at the history.pushState documentation, you can see that if the state changes - meaning that we moved to a different URL - the popstate event is triggered. We'll use that to fix that issue. ### Task To make sure the displayed template is updated when the browser history changes, we'll attach a new function that calls updateRoute(). We'll do that at the bottom of our app.js file: Understanding this history integration: - Listens for popstate events that occur when users navigate with browser buttons - Uses an arrow function for concise event handler syntax - Calls updateRoute() automatically whenever the history state changes - Initializes the app by calling updateRoute() when the page first loads - Ensures the correct template displays regardless of how users navigate Here's a refresher video on arrow functions: [](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions") Now try to use the back and forward buttons of your browsers, and check that the displayed route is correctly updated this time. ### β‘ What You Can Do in the Next 5 Minutes - [ ] Test your banking app's navigation using browser back/forward buttons - [ ] Try manually typing different URLs in the address bar to test routing - [ ] Open browser DevTools and inspect how templates are cloned into the DOM - [ ] Experiment with adding console.log statements to track the routing flow ### π― What You Can Accomplish This Hour - [ ] Complete the post-lesson quiz and understand SPA architecture concepts - [ ] Add CSS styling to make your banking app templates look professional - [ ] Implement the 404 error page challenge with proper error handling - [ ] Create the credits page challenge with additional routing functionality - [ ] Add loading states and transitions between template switches ### π Your Week-Long SPA Development Journey - [ ] Complete the full banking app with forms, data management, and persistence - [ ] Add advanced routing features like route parameters and nested routes - [ ] Implement navigation guards and authentication-based routing - [ ] Create reusable template components and a component library - [ ] Add animations and transitions for smoother user experience - [ ] Deploy your SPA to a hosting platform and configure routing properly ### π Your Month-Long Frontend Architecture Mastery - [ ] Build complex SPAs using modern frameworks like React, Vue, or Angular - [ ] Learn advanced state management patterns and libraries - [ ] Master build tools and development workflows for SPA development - [ ] Implement Progressive Web App features and offline functionality - [ ] Study performance optimization techniques for large-scale SPAs - [ ] Contribute to open source SPA projects and share your knowledge ## π― Your Single-Page Application Mastery Timeline ### π οΈ Your SPA Development Toolkit Summary After completing this lesson, you now have mastered: - Template Architecture: Reusable HTML components with dynamic content rendering - Client-side Routing: URL management and navigation without page reloads - Browser Integration: History API usage and back/forward button support - Event-driven Systems: Navigation handling and user interaction management - DOM Manipulation: Template cloning, content switching, and element management - Error Handling: Graceful fallbacks for invalid routes and missing content - Performance Patterns: Efficient content loading and rendering strategies Real-World Applications: Your SPA development skills apply directly to: - Modern Web Applications: React, Vue, Angular, and other framework development - Progressive Web Apps: Offline-capable applications with app-like experiences - Enterprise Dashboards: Complex business applications with multiple views - E-commerce Platforms: Product catalogs, shopping carts, and checkout flows - Content Management: Dynamic content creation and editing interfaces - Mobile Development: Hybrid apps using web technologies Professional Skills Gained: You can now: - Architect single-page applications with proper separation of concerns - Implement client-side routing systems that scale with application complexity - Debug complex navigation flows using browser developer tools - Optimize application performance through efficient template management - Design user experiences that feel native and responsive Frontend Development Concepts Mastered: - Component Architecture: Reusable UI patterns and template systems - State Synchronization: URL state management and browser history - Event-driven Programming: User interaction handling and navigation - Performance Optimization: Efficient DOM manipulation and content loading - User Experience Design: Smooth transitions and intuitive navigation Next Level: You're ready to explore modern frontend frameworks, advanced state management, or build complex enterprise applications! π Achievement Unlocked: You've built a professional single-page application foundation with modern web architecture patterns! --- ## GitHub Copilot Agent Challenge π Use the Agent mode to complete the following challenge: Description: Enhance the banking app by implementing error handling and a 404 page template for invalid routes, improving the user experience when navigating to non-existent pages. Prompt: Create a new HTML template with id "not-found" that displays a user-friendly 404 error page with styling. Then modify the JavaScript routing logic to show this template when users navigate to invalid URLs, and add a "Go Home" button that navigates back to the login page. Learn more about agent mode here. ## π Challenge Add a new template and route for a third page that shows the credits for this app. Challenge goals: - Create a new HTML template with appropriate content structure - Add the new route to your routes configuration object - Include navigation links to and from the credits page - Test that all navigation works correctly with browser history ## Post-Lecture Quiz Post-lecture quiz ## Review & Self Study Routing is one of the surprisingly tricky parts of web development, especially as the web moves from page refresh behaviors to Single Page Application page refreshes. Read a little about how the Azure Static Web App service handles routing. Can you explain why some of the decisions described on that document are necessary? Additional learning resources: - Explore how popular frameworks like React Router and Vue Router implement client-side routing - Research the differences between hash-based routing and history API routing - Learn about server-side rendering (SSR) and how it affects routing strategies - Investigate how Progressive Web Apps (PWAs) handle routing and navigation ## Assignment Improve the routing
Build a Banking App Part 2: Build a Login and Registration Form
## Pre-Lecture Quiz Pre-lecture quiz Ever filled out a form online and had it reject your email format? Or lost all your information when you clicked submit? We've all encountered these frustrating experiences. Forms are the bridge between your users and your application's functionality. Like the careful protocols that air traffic controllers use to guide planes safely to their destinations, well-designed forms provide clear feedback and prevent costly errors. Poor forms, on the other hand, can drive users away faster than a miscommunication in a busy airport. In this lesson, we'll transform your static banking app into an interactive application. You'll learn to build forms that validate user input, communicate with servers, and provide helpful feedback. Think of it as building the control interface that lets users navigate your application's features. By the end, you'll have a complete login and registration system with validation that guides users toward success rather than frustration. ## Prerequisites Before we start building forms, let's make sure you've got everything set up correctly. This lesson picks up right where we left off in the previous one, so if you skipped ahead, you might want to go back and get the basics working first. ### Required Setup ### Server Configuration Your development environment will include: - Frontend server: Serves your banking app (typically port 3000) - Backend API server: Handles data storage and retrieval (port 5000) - Both servers can run simultaneously without conflicts Testing your API connection: If you see the API version response, you're ready to proceed! --- ## Understanding HTML Forms and Controls HTML forms are how users communicate with your web application. Think of them as the telegraph system that connected distant places in the 19th century β they're the communication protocol between user intent and application response. When designed thoughtfully, they catch errors, guide input formatting, and provide helpful suggestions. Modern forms are significantly more sophisticated than basic text inputs. HTML5 introduced specialized input types that handle email validation, number formatting, and date selection automatically. These improvements benefit both accessibility and mobile user experiences. ### Essential Form Elements Building blocks every form needs: Here's what this code does: - Creates a form container with a unique identifier - Specifies the HTTP method for data submission - Associates labels with inputs for accessibility - Defines a submit button to process the form ### Modern Input Types and Attributes ### Button Types and Behavior Here's what each button type does: - Submit buttons: Trigger form submission and send data to the specified endpoint - Reset buttons: Restore all form fields to their initial state - Regular buttons: Provide no default behavior, requiring custom JavaScript for functionality ### Building Your Login Form Now let's create a practical login form that demonstrates modern HTML form practices. We'll start with a basic structure and gradually enhance it with accessibility features and validation. Breaking down what happens here: - Structures the form with semantic HTML5 elements - Groups related elements using div containers with meaningful classes - Associates labels with inputs using the for and id attributes - Includes modern attributes like autocomplete and placeholder for better UX - Adds novalidate to handle validation with JavaScript instead of browser defaults ### The Power of Proper Labels Why labels matter for modern web development: What proper labels accomplish: - Enables screen readers to announce form fields clearly - Expands the clickable area (clicking the label focuses the input) - Improves mobile usability with larger touch targets - Supports form validation with meaningful error messages - Enhances SEO by providing semantic meaning to form elements ### Creating the Registration Form The registration form requires more detailed information to create a complete user account. Let's build it with modern HTML5 features and enhanced accessibility. In the above, we've: - Organized each field in container divs for better styling and layout - Added appropriate autocomplete attributes for browser autofill support - Included helpful placeholder text to guide user input - Set sensible defaults using the value attribute - Applied validation attributes like required, maxlength, and min - Used type="number" for the balance field with decimal support ### Exploring Input Types and Behavior Modern input types provide enhanced functionality: ### π Pedagogical Check-in Form Foundation Understanding: Before implementing JavaScript, ensure you understand: - β How semantic HTML creates accessible form structures - β Why input types matter for mobile keyboards and validation - β The relationship between labels and form controls - β How form attributes affect default browser behavior Quick Self-Test: What happens if you submit a form without JavaScript handling? Answer: The browser performs default submission, usually redirecting to the action URL HTML5 Form Benefits: Modern forms provide: - Built-in Validation: Automatic email and number format checking - Mobile Optimization: Appropriate keyboards for different input types - Accessibility: Screen reader support and keyboard navigation - Progressive Enhancement: Works even when JavaScript is disabled ## Understanding Form Submission Methods When someone fills out your form and hits submit, that data needs to go somewhere β usually to a server that can save it. There are a couple of different ways this can happen, and knowing which one to use can save you from some headaches later. Let's take a look at what actually happens when someone clicks that submit button. ### Default Form Behavior First, let's observe what happens with basic form submission: Test your current forms: 1. Click the Register button in your form 2. Observe the changes in your browser's address bar 3. Notice how the page reloads and data appears in the URL ### HTTP Methods Comparison Understanding the differences: Understanding the fundamental differences: - GET: Appends form data to the URL as query parameters (appropriate for search operations) - POST: Includes data in the request body (essential for sensitive information) - GET limitations: Size constraints, visible data, persistent browser history - POST advantages: Large data capacity, privacy protection, file upload support ### Configuring Form Submission Let's configure your registration form to communicate properly with the backend API using the POST method: Here's what this configuration does: - Directs form submission to your API endpoint - Uses POST method for secure data transmission - Includes novalidate to handle validation with JavaScript ### Testing Form Submission Follow these steps to test your form: 1. Fill out the registration form with your information 2. Click the "Create Account" button 3. Observe the server response in your browser What you should see: - Browser redirects to the API endpoint URL - JSON response containing your newly created account data - Server confirmation that the account was successfully created ### Understanding JSON Responses When the server processes your form successfully: This response confirms: - Creates a new account with your specified data - Assigns a unique identifier for future reference - Returns all account information for verification - Indicates successful database storage ## Modern Form Handling with JavaScript Traditional form submissions cause full page reloads, similar to how early space missions required complete system resets for course corrections. This approach disrupts the user experience and loses application state. JavaScript form handling works like the continuous guidance systems used by modern spacecraft β making real-time adjustments without losing navigation context. We can intercept form submissions, provide immediate feedback, handle errors gracefully, and update the interface based on server responses while maintaining the user's position in the application. ### Why Avoid Page Reloads? Benefits of JavaScript form handling: - Maintains application state and user context - Provides instant feedback and loading indicators - Enables dynamic error handling and validation - Creates smooth, app-like user experiences - Allows conditional logic based on server responses ### Transitioning from Traditional to Modern Forms Traditional approach challenges: - Redirects users away from your application - Loses current application state and context - Requires full page reloads for simple operations - Provides limited control over user feedback Modern JavaScript approach advantages: - Keeps users within your application - Maintains all application state and data - Enables real-time validation and feedback - Supports progressive enhancement and accessibility ### Implementing JavaScript Form Handling Let's replace the traditional form submission with modern JavaScript event handling: Add the registration logic to your app.js file: Breaking down what happens here: - Prevents default form submission using event.preventDefault() - Retrieves the form element using modern DOM selection - Extracts form data using the powerful FormData API - Converts FormData to a plain object with Object.fromEntries() - Serializes the data to JSON format for server communication - Logs the processed data for debugging and verification ### Understanding the FormData API The FormData API provides powerful form handling: FormData API advantages: - Comprehensive collection: Captures all form elements including text, files, and complex inputs - Type awareness: Handles different input types automatically without custom coding - Efficiency: Eliminates manual field collection with single API call - Adaptability: Maintains functionality as form structure evolves ### Creating the Server Communication Function Now let's build a robust function to communicate with your API server using modern JavaScript patterns: Understanding asynchronous JavaScript: What this modern implementation accomplishes: - Uses async/await for readable asynchronous code - Includes proper error handling with try/catch blocks - Checks response status before processing data - Sets appropriate headers for JSON communication - Provides detailed error messages for debugging - Returns consistent data structure for success and error cases ### The Power of Modern Fetch API Fetch API advantages over older methods: Key concepts for server communication: - Async functions allow pausing execution to wait for server responses - Await keyword makes asynchronous code read like synchronous code - Fetch API provides modern, promise-based HTTP requests - Error handling ensures your app responds gracefully to network issues ### Completing the Registration Function Let's bring everything together with a complete, production-ready registration function: This enhanced implementation includes: - Provides visual feedback during form submission - Disables the submit button to prevent duplicate submissions - Handles both expected and unexpected errors gracefully - Shows user-friendly success and error messages - Resets the form after successful registration - Restores UI state regardless of outcome ### Testing Your Implementation Open your browser developer tools and test the registration: 1. Open the browser console (F12 β Console tab) 2. Fill out the registration form 3. Click "Create Account" 4. Observe the console messages and user feedback What you should see: - Loading state appears on the submit button - Console logs show detailed information about the process - Success message appears when account creation succeeds - Form resets automatically after successful submission ### π Pedagogical Check-in Modern JavaScript Integration: Verify your understanding of asynchronous form handling: - β How does event.preventDefault() change default form behavior? - β Why is the FormData API more efficient than manual field collection? - β How do async/await patterns improve code readability? - β What role does error handling play in user experience? System Architecture: Your form handling demonstrates: - Event-Driven Programming: Forms respond to user actions without page reloads - Asynchronous Communication: Server requests don't block the user interface - Error Handling: Graceful degradation when network requests fail - State Management: UI updates reflect server responses appropriately - Progressive Enhancement: Base functionality works, JavaScript enhances it Professional Patterns: You've implemented: - Single Responsibility: Functions have clear, focused purposes - Error Boundaries: Try/catch blocks prevent application crashes - User Feedback: Loading states and success/error messaging - Data Transformation: FormData to JSON for server communication ## Comprehensive Form Validation Form validation prevents the frustrating experience of discovering errors only after submission. Like the multiple redundant systems on the International Space Station, effective validation employs multiple layers of safety checks. The optimal approach combines browser-level validation for immediate feedback, JavaScript validation for enhanced user experience, and server-side validation for security and data integrity. This redundancy ensures both user satisfaction and system protection. ### Understanding Validation Layers Multi-layer validation strategy: - HTML5 validation: Immediate browser-based checks - JavaScript validation: Custom logic and user experience - Server validation: Final security and data integrity checks - Progressive enhancement: Works even if JavaScript is disabled ### HTML5 Validation Attributes Modern validation tools at your disposal: ### CSS Validation Styling Create visual feedback for validation states: What these visual cues accomplish: - Green borders: Indicate successful validation, like green lights in mission control - Red borders: Signal validation errors requiring attention - Focus highlights: Provide clear visual context for current input location - Consistent styling: Establish predictable interface patterns users can learn ### Implementing Comprehensive Validation Let's enhance your registration form with robust validation that provides excellent user experience and data quality: Understanding the enhanced validation: - Combines required field indicators with helpful descriptions - Includes pattern attributes for format validation - Provides title attributes for accessibility and tooltips - Adds helper text to guide user input - Uses semantic HTML structure for better accessibility ### Advanced Validation Rules What each validation rule accomplishes: ### Testing Validation Behavior Try these validation scenarios: 1. Submit the form with empty required fields 2. Enter a username shorter than 3 characters 3. Try special characters in the username field 4. Input a negative balance amount What you'll observe: - Browser displays native validation messages - Styling changes based on :valid and :invalid states - Form submission is prevented until all validations pass - Focus automatically moves to the first invalid field ### Client-Side vs Server-Side Validation Why you need both layers: - Client-side validation: Provides immediate feedback and improves user experience - Server-side validation: Ensures security and handles complex business rules - Combined approach: Creates robust, user-friendly, and secure applications - Progressive enhancement: Works even when JavaScript is disabled ### β‘ What You Can Do in the Next 5 Minutes - [ ] Test your form with invalid data to see validation messages - [ ] Try submitting the form with JavaScript disabled to see HTML5 validation - [ ] Open browser DevTools and inspect form data being sent to the server - [ ] Experiment with different input types to see mobile keyboard changes ### π― What You Can Accomplish This Hour - [ ] Complete the post-lesson quiz and understand form handling concepts - [ ] Implement the comprehensive validation challenge with real-time feedback - [ ] Add CSS styling to create professional-looking forms - [ ] Create error handling for duplicate usernames and server errors - [ ] Add password confirmation fields with matching validation ### π Your Week-Long Form Mastery Journey - [ ] Complete the full banking app with advanced form features - [ ] Implement file upload capabilities for profile pictures or documents - [ ] Add multi-step forms with progress indicators and state management - [ ] Create dynamic forms that adapt based on user selections - [ ] Implement form autosave and recovery for better user experience - [ ] Add advanced validation like email verification and phone number formatting ### π Your Month-Long Frontend Development Mastery - [ ] Build complex form applications with conditional logic and workflows - [ ] Learn form libraries and frameworks for rapid development - [ ] Master accessibility guidelines and inclusive design principles - [ ] Implement internationalization and localization for global forms - [ ] Create reusable form component libraries and design systems - [ ] Contribute to open source form projects and share best practices ## π― Your Form Development Mastery Timeline ### π οΈ Your Form Development Toolkit Summary After completing this lesson, you now have mastered: - HTML5 Forms: Semantic structure, input types, and accessibility features - JavaScript Form Handling: Event management, data collection, and AJAX communication - Validation Architecture: Multi-layer validation for security and user experience - Asynchronous Programming: Modern fetch API and async/await patterns - Error Management: Comprehensive error handling and user feedback systems - User Experience Design: Loading states, success messaging, and error recovery - Progressive Enhancement: Forms that work across all browsers and capabilities Real-World Applications: Your form development skills apply directly to: - E-commerce Applications: Checkout processes, account registration, and payment forms - Enterprise Software: Data entry systems, reporting interfaces, and workflow applications - Content Management: Publishing platforms, user-generated content, and administrative interfaces - Financial Applications: Banking interfaces, investment platforms, and transaction systems - Healthcare Systems: Patient portals, appointment scheduling, and medical record forms - Educational Platforms: Course registration, assessment tools, and learning management Professional Skills Gained: You can now: - Design accessible forms that work for all users including those with disabilities - Implement secure form validation that prevents data corruption and security vulnerabilities - Create responsive user interfaces that provide clear feedback and guidance - Debug complex form interactions using browser developer tools and network analysis - Optimize form performance through efficient data handling and validation strategies Frontend Development Concepts Mastered: - Event-Driven Architecture: User interaction handling and response systems - Asynchronous Programming: Non-blocking server communication and error handling - Data Validation: Client-side and server-side security and integrity checks - User Experience Design: Intuitive interfaces that guide users toward success - Accessibility Engineering: Inclusive design that works for diverse user needs Next Level: You're ready to explore advanced form libraries, implement complex validation rules, or build enterprise-grade data collection systems! π Achievement Unlocked: You've built a complete form handling system with professional validation, error handling, and user experience patterns! --- --- ## GitHub Copilot Agent Challenge π Use the Agent mode to complete the following challenge: Description: Enhance the registration form with comprehensive client-side validation and user feedback. This challenge will help you practice form validation, error handling, and improving user experience with interactive feedback. Prompt: Create a complete form validation system for the registration form that includes: 1) Real-time validation feedback for each field as the user types, 2) Custom validation messages that appear below each input field, 3) A password confirmation field with matching validation, 4) Visual indicators (like green checkmarks for valid fields and red warnings for invalid ones), 5) A submit button that only becomes enabled when all validations pass. Use HTML5 validation attributes, CSS for styling the validation states, and JavaScript for the interactive behavior. Learn more about agent mode here. ## π Challenge Show an error message in the HTML if the user already exists. Here's an example of what the final login page can look like after a bit of styling: ## Post-Lecture Quiz Post-lecture quiz ## Review & Self Study Developers have gotten very creative about their form building efforts, especially regarding validation strategies. Learn about different form flows by looking through CodePen; can you find some interesting and inspiring forms? ## Assignment Style your bank app
Build a Banking App Part 3: Methods of Fetching and Using Data
Think about the Enterprise's computer in Star Trek - when Captain Picard asks for ship status, the information appears instantly without the whole interface shutting down and rebuilding itself. That seamless flow of information is exactly what we're building here with dynamic data fetching. Right now, your banking app is like a printed newspaper - informative but static. We're going to transform it into something more like mission control at NASA, where data flows continuously and updates in real-time without interrupting the user's workflow. You'll learn how to communicate with servers asynchronously, handle data that arrives at different times, and transform raw information into something meaningful for your users. This is the difference between a demo and production-ready software. ## β‘ What You Can Do in the Next 5 Minutes Quick Start Pathway for Busy Developers - Minute 1-2: Start your API server (cd api && npm start) and test the connection - Minute 3: Create a basic getAccount() function using fetch - Minute 4: Wire up the login form with action="javascript:login()" - Minute 5: Test login and watch account data appear in the console Quick Test Commands: Why This Matters: In 5 minutes, you'll see the magic of asynchronous data fetching that powers every modern web application. This is the foundation that makes apps feel responsive and alive. ## πΊοΈ Your Learning Journey Through Data-Driven Web Applications Your Journey Destination: By the end of this lesson, you'll understand how modern web applications fetch, process, and display data dynamically, creating the seamless user experiences we expect from professional applications. ## Pre-Lecture Quiz Pre-lecture quiz ### Prerequisites Before diving into data fetching, ensure you have these components ready: - Previous Lesson: Complete the Login and Registration Form - we'll build on this foundation - Local Server: Install Node.js and run the server API to provide account data - API Connection: Test your server connection with this command: This quick test ensures all components are communicating properly: - Verifies that Node.js is running correctly on your system - Confirms your API server is active and responding - Validates that your app can reach the server (like checking radio contact before a mission) ## π§ Data Management Ecosystem Overview Core Principle: Modern web applications are data orchestration systems - they coordinate between user interfaces, server APIs, and browser security models to create seamless, responsive experiences. --- ## Understanding Data Fetching in Modern Web Apps The way web applications handle data has evolved dramatically over the past two decades. Understanding this evolution will help you appreciate why modern techniques like AJAX and the Fetch API are so powerful and why they've become essential tools for web developers. Let's explore how traditional websites worked compared to the dynamic, responsive applications we build today. ### Traditional Multi-Page Applications (MPA) In the early days of the web, every click was like changing channels on an old television - the screen would go blank, then slowly tune into the new content. This was the reality of early web applications, where every interaction meant completely rebuilding the entire page from scratch. Why this approach felt clunky: - Every click meant rebuilding the entire page from scratch - Users got interrupted mid-thought by those annoying page flashes - Your internet connection worked overtime downloading the same header and footer repeatedly - Apps felt more like clicking through a filing cabinet than using software ### Modern Single-Page Applications (SPA) AJAX (Asynchronous JavaScript and XML) changed this paradigm entirely. Like the modular design of the International Space Station, where astronauts can replace individual components without rebuilding the entire structure, AJAX allows us to update specific parts of a webpage without reloading everything. Despite the name mentioning XML, we mostly use JSON today, but the core principle remains: update only what needs to change. Why SPAs feel so much better: - Only the parts that actually changed get updated (smart, right?) - No more jarring interruptions - your users stay in their flow - Less data traveling over the wire means faster loading - Everything feels snappy and responsive, like the apps on your phone ### The Evolution to Modern Fetch API Modern browsers provide the Fetch API, which replaces the older XMLHttpRequest. Like the difference between operating a telegraph and using email, Fetch API uses promises for cleaner asynchronous code and handles JSON naturally. The bottom line: - Works great in Chrome, Firefox, Safari, and Edge (basically everywhere your users are) - Only Internet Explorer needs extra help (and honestly, it's time to let IE go) - Sets you up perfectly for the elegant async/await patterns we'll use later ### Implementing User Login and Data Retrieval Now let's implement the login system that transforms your banking app from a static display into a functional application. Like the authentication protocols used in secure military facilities, we'll verify user credentials and then provide access to their specific data. We'll build this incrementally, starting with basic authentication and then adding the data-fetching capabilities. #### Step 1: Create the Login Function Foundation Open your app.js file and add a new login function. This will handle the user authentication process: Let's break this down: - That async keyword? It's telling JavaScript "hey, this function might need to wait for things" - We're grabbing our form from the page (nothing fancy, just finding it by its ID) - Then we're pulling out whatever the user typed as their username - Here's a neat trick: you can access any form input by its name attribute - no need for extra getElementById calls! #### Step 2: Create the Account Data Fetching Function Next, we'll create a dedicated function to retrieve account data from the server. This follows the same pattern as your registration function but focuses on data retrieval: Here's what this code accomplishes: - Uses the modern fetch API to request data asynchronously - Constructs a GET request URL with the username parameter - Applies encodeURIComponent() to safely handle special characters in URLs - Converts the response to JSON format for easy data manipulation - Handles errors gracefully by returning an error object instead of crashing Why this matters: - Prevents special characters from breaking URLs - Protects against URL manipulation attacks - Ensures your server receives the intended data - Follows secure coding practices #### Understanding HTTP GET Requests Here's something that might surprise you: when you use fetch without any extra options, it automatically creates a GET request. This is perfect for what we're doing - asking the server "hey, can I see this user's account data?" Think of GET requests like politely asking to borrow a book from the library - you're requesting to see something that already exists. POST requests (which we used for registration) are more like submitting a new book to be added to the collection. #### Step 3: Bringing It All Together Now for the satisfying part - let's connect your account fetching function to the login process. This is where everything clicks into place: This function follows a clear sequence: - Extract the username from the form input - Request the user's account data from the server - Handle any errors that occur during the process - Store the account data and navigate to the dashboard upon success #### Step 4: Creating a Home for Your Data Your app needs somewhere to remember the account information once it's loaded. Think of this like your app's short-term memory - a place to keep the current user's data handy. Add this line at the top of your app.js file: Why we need this: - Keeps the account data accessible from anywhere in your app - Starting with null means "no one's logged in yet" - Gets updated when someone successfully logs in or registers - Acts like a single source of truth - no confusion about who's logged in #### Step 5: Wire Up Your Form Now let's connect your shiny new login function to your HTML form. Update your form tag like this: What this little change does: - Stops the form from doing its default "reload the whole page" behavior - Calls your custom JavaScript function instead - Keeps everything smooth and single-page-app-like - Gives you complete control over what happens when users hit "Login" #### Step 6: Enhance Your Registration Function For consistency, update your register function to also store account data and navigate to the dashboard: This enhancement provides: - Seamless transition from registration to dashboard - Consistent user experience between login and registration flows - Immediate access to account data after successful registration #### Testing Your Implementation Time to take it for a spin: 1. Create a new account to make sure everything's working 2. Try logging in with those same credentials 3. Peek at your browser's console (F12) if anything seems off 4. Make sure you land on the dashboard after a successful login If something's not working, don't panic! Most issues are simple fixes like typos or forgetting to start the API server. #### A Quick Word About Cross-Origin Magic You might be wondering: "How is my web app talking to this API server when they're running on different ports?" Great question! This touches on something every web developer bumps into eventually. In our setup: - Your web app runs on localhost:3000 (development server) - Your API server runs on localhost:5000 (backend server) - The API server includes CORS headers that explicitly authorize communication from your web app This configuration mirrors real-world development where frontend and backend applications typically run on separate servers. ## Bringing Your Data to Life in HTML Now we'll make the fetched data visible to users through DOM manipulation. Like the process of developing photographs in a darkroom, we're taking invisible data and rendering it into something users can see and interact with. DOM manipulation is the technique that transforms static web pages into dynamic applications that update their content based on user interactions and server responses. ### Choosing the Right Tool for the Job When it comes to updating your HTML with JavaScript, you've got several options. Think of these like different tools in a toolbox - each one perfect for specific jobs: #### The Safe Way to Show Text: textContent The textContent property is your best friend when displaying user data. It's like having a bouncer for your webpage - nothing harmful gets through: Benefits of textContent: - Treats everything as plain text (prevents script execution) - Automatically clears existing content - Efficient for simple text updates - Provides built-in security against malicious content #### Creating Dynamic HTML Elements For more complex content, combine document.createElement() with the append() method: Understanding this approach: - Creates new DOM elements programmatically - Maintains full control over element attributes and content - Allows for complex, nested element structures - Preserves security by separating structure from content Risks of innerHTML: - Executes any <script> tags in user data - Vulnerable to code injection attacks - Creates potential security vulnerabilities - The safer alternatives we're using provide equivalent functionality ### Making Errors User-Friendly Currently, login errors only appear in the browser console, which is invisible to users. Like the difference between a pilot's internal diagnostics and the passenger information system, we need to communicate important information through the appropriate channel. Implementing visible error messages provides users with immediate feedback about what went wrong and how to proceed. #### Step 1: Add a Spot for Error Messages First, let's give error messages a home in your HTML. Add this right before your login button so users will see it naturally: What's happening here: - We're creating an empty container that stays invisible until needed - It's positioned where users naturally look after clicking "Login" - That role="alert" is a nice touch for screen readers - it tells assistive technology "hey, this is important!" - The unique id gives our JavaScript an easy target #### Step 2: Create a Handy Helper Function Let's make a little utility function that can update any element's text. This is one of those "write once, use everywhere" functions that'll save you time: Function benefits: - Simple interface requiring only an element ID and text content - Safely locates and updates DOM elements - Reusable pattern that reduces code duplication - Maintains consistent updating behavior across the application #### Step 3: Show Errors Where Users Can See Them Now let's replace that hidden console message with something users can actually see. Update your login function: This small change makes a big difference: - Error messages appear right where users are looking - No more mysterious silent failures - Users get immediate, actionable feedback - Your app starts feeling professional and thoughtful Now when you test with an invalid account, you'll see a helpful error message right on the page! #### Step 4: Being Inclusive with Accessibility Here's something cool about that role="alert" we added earlier - it's not just decoration! This little attribute creates what's called a Live Region that immediately announces changes to screen readers: Why this matters: - Screen reader users hear the error message as soon as it appears - Everyone gets the same important information, regardless of how they navigate - It's a simple way to make your app work for more people - Shows you care about creating inclusive experiences Small touches like this separate good developers from great ones! ### π― Pedagogical Check-in: Authentication Patterns Pause and Reflect: You've just implemented a complete authentication flow. This is a foundational pattern in web development. Quick Self-Assessment: - Can you explain why we use async/await for API calls? - What would happen if we forgot the encodeURIComponent() function? - How does our error handling improve the user experience? Real-World Connection: The patterns you've learned here (async data fetching, error handling, user feedback) are used in every major web application from social media platforms to e-commerce sites. You're building production-level skills! Challenge Question: How might you modify this authentication system to handle multiple user roles (customer, admin, teller)? Think about the data structure and UI changes needed. #### Step 5: Apply the Same Pattern to Registration For consistency, implement identical error handling in your registration form: 1. Add an error display element to your registration HTML: 2. Update your register function to use the same error display pattern: Benefits of consistent error handling: - Provides uniform user experience across all forms - Reduces cognitive load by using familiar patterns - Simplifies maintenance with reusable code - Ensures accessibility standards are met throughout the app ## Creating Your Dynamic Dashboard Now we'll transform your static dashboard into a dynamic interface that displays real account data. Like the difference between a printed flight schedule and the live departure boards at airports, we're moving from static information to real-time, responsive displays. Using the DOM manipulation techniques you've learned, we'll create a dashboard that updates automatically with current account information. ### Getting to Know Your Data Before we start building, let's peek at what kind of data your server sends back. When someone successfully logs in, here's the treasure trove of information you get to work with: This data structure provides: - user: Perfect for personalizing the experience ("Welcome back, Sarah!") - currency: Makes sure we display money amounts correctly - description: A friendly name for the account - balance: The all-important current balance - transactions: The complete transaction history with all the details Everything you need to build a professional-looking banking dashboard! Why the test account is handy: - Comes with realistic sample data already loaded - Perfect for seeing how transactions display - Great for testing your dashboard features - Saves you from having to create dummy data manually ### Creating the Dashboard Display Elements Let's build your dashboard interface step by step, starting with the account summary information and then moving on to more complex features like transaction lists. #### Step 1: Update Your HTML Structure First, replace the static "Balance" section with dynamic placeholder elements that your JavaScript can populate: Next, add a section for the account description. Since this acts as a title for the dashboard content, use semantic HTML: Understanding the HTML structure: - Uses separate <span> elements for balance and currency for individual control - Applies unique IDs to each element for JavaScript targeting - Follows semantic HTML by using <h2> for the account description - Creates a logical hierarchy for screen readers and SEO #### Step 2: Create the Dashboard Update Function Now create a function that populates your dashboard with real account data: Step by step, here's what this function does: - Validates that account data exists before proceeding - Redirects unauthenticated users back to the login page - Updates the account description using the reusable updateElement function - Formats the balance to always show two decimal places - Displays the appropriate currency symbol #### Step 3: Making Sure Your Dashboard Updates To ensure your dashboard refreshes with current data every time someone visits it, we need to hook into your navigation system. If you completed the lesson 1 assignment, this should feel familiar. If not, don't worry - here's what you need: Add this to the end of your updateRoute() function: Then update your routes to include the dashboard initialization: What this clever setup does: - Checks if a route has special initialization code - Runs that code automatically when the route loads - Ensures your dashboard always shows fresh, current data - Keeps your routing logic clean and organized #### Testing Your Dashboard After implementing these changes, test your dashboard: 1. Log in with a test account 2. Verify you're redirected to the dashboard 3. Check that the account description, balance, and currency display correctly 4. Try logging out and back in to ensure data refreshes properly Your dashboard should now display dynamic account information that updates based on the logged-in user's data! ## Building Smart Transaction Lists with Templates Instead of manually creating HTML for each transaction, we'll use templates to generate consistent formatting automatically. Like the standardized components used in spacecraft manufacturing, templates ensure every transaction row follows the same structure and appearance. This technique scales efficiently from a few transactions to thousands, maintaining consistent performance and presentation. ### Step 1: Create the Transaction Template First, add a reusable template for transaction rows in your HTML <body>: Understanding HTML templates: - Defines the structure for a single table row - Remains invisible until cloned and populated with JavaScript - Includes three cells for date, description, and amount - Provides a reusable pattern for consistent formatting ### Step 2: Prepare Your Table for Dynamic Content Next, add an id to your table body so JavaScript can easily target it: What this accomplishes: - Creates a clear target for inserting transaction rows - Separates the table structure from the dynamic content - Enables easy clearing and repopulating of transaction data ### Step 3: Build the Transaction Row Factory Function Now create a function that transforms transaction data into HTML elements: Breaking down this factory function: - Retrieves the template element by its ID - Clones the template content for safe manipulation - Selects the table row within the cloned content - Populates each cell with transaction data - Formats the amount to show proper decimal places - Returns the completed row ready for insertion ### Step 4: Generate Multiple Transaction Rows Efficiently Add this code to your updateDashboard() function to display all transactions: Understanding this efficient approach: - Creates a document fragment to batch DOM operations - Iterates through all transactions in the account data - Generates a row for each transaction using the factory function - Collects all rows in the fragment before adding to the DOM - Performs a single DOM update instead of multiple individual insertions ### Step 5: Enhance the Update Function for Mixed Content Your updateElement() function currently only handles text content. Update it to work with both text and DOM nodes: Key improvements in this update: - Clears existing content before adding new content - Accepts either text strings or DOM nodes as parameters - Uses the append() method for flexibility - Maintains backward compatibility with existing text-based usage ### Taking Your Dashboard for a Test Drive Time for the moment of truth! Let's see your dynamic dashboard in action: 1. Log in using the test account (it has sample data ready to go) 2. Navigate to your dashboard 3. Check that transaction rows appear with proper formatting 4. Make sure dates, descriptions, and amounts all look good If everything's working, you should see a fully functional transaction list on your dashboard! π What you've accomplished: - Built a dashboard that scales with any amount of data - Created reusable templates for consistent formatting - Implemented efficient DOM manipulation techniques - Developed functionality comparable to production banking applications You've successfully transformed a static webpage into a dynamic web application. ### π― Pedagogical Check-in: Dynamic Content Generation Architecture Understanding: You've implemented a sophisticated data-to-UI pipeline that mirrors patterns used in frameworks like React, Vue, and Angular. Key Concepts Mastered: - Template-based rendering: Creating reusable UI components - Document fragments: Optimizing DOM performance - Safe DOM manipulation: Preventing security vulnerabilities - Data transformation: Converting server data to user interfaces Industry Connection: These techniques form the foundation of modern frontend frameworks. React's virtual DOM, Vue's template system, and Angular's component architecture all build on these core concepts. Reflection Question: How would you extend this system to handle real-time updates (like new transactions appearing automatically)? Consider WebSockets or Server-Sent Events. --- ## π Your Data Management Mastery Timeline π Graduation Milestone: You've successfully built a complete data-driven web application using modern JavaScript patterns. These skills translate directly to working with frameworks like React, Vue, or Angular. π Next Level Capabilities: - Ready to explore frontend frameworks that build on these concepts - Prepared to implement real-time features with WebSockets - Equipped to build Progressive Web Apps with offline capabilities - Foundation set for learning advanced state management patterns ## GitHub Copilot Agent Challenge π Use the Agent mode to complete the following challenge: Description: Enhance the banking app by implementing a transaction search and filter feature that allows users to find specific transactions by date range, amount, or description. Prompt: Create a search functionality for the banking app that includes: 1) A search form with input fields for date range (from/to), minimum/maximum amount, and transaction description keywords, 2) A filterTransactions() function that filters the account.transactions array based on the search criteria, 3) Update the updateDashboard() function to show filtered results, and 4) Add a "Clear Filters" button to reset the view. Use modern JavaScript array methods like filter() and handle edge cases for empty search criteria. Learn more about agent mode here. ## π Challenge Ready to take your banking app to the next level? Let's make it look and feel like something you'd actually want to use. Here are some ideas to spark your creativity: Make it beautiful: Add CSS styling to transform your functional dashboard into something visually appealing. Think clean lines, good spacing, and maybe even some subtle animations. Make it responsive: Try using media queries to create a responsive design that works great on phones, tablets, and desktops. Your users will thank you! Add some flair: Consider color-coding transactions (green for income, red for expenses), adding icons, or creating hover effects that make the interface feel interactive. Here's what a polished dashboard could look like: Don't feel like you have to match this exactly - use it as inspiration and make it your own! ## Post-Lecture Quiz Post-lecture quiz ## Assignment Refactor and comment your code
Build a Banking App Part 4: Concepts of State Management
## β‘ What You Can Do in the Next 5 Minutes Quick Start Pathway for Busy Developers - Minute 1: Test the current state issue - log in, refresh page, observe logout - Minute 2: Replace let account = null with let state = { account: null } - Minute 3: Create a simple updateState() function for controlled updates - Minute 4: Update one function to use the new pattern - Minute 5: Test the improved predictability and debugging capability Quick Diagnostic Test: Why This Matters: In 5 minutes, you'll experience the transformation from chaotic state management to predictable, debuggable patterns. This is the foundation that makes complex applications maintainable. ## πΊοΈ Your Learning Journey Through State Management Mastery Your Journey Destination: By the end of this lesson, you'll have built a professional-grade state management system that handles persistence, data freshness, and predictable updates - the same patterns used in production applications. ## Pre-Lecture Quiz Pre-lecture quiz ## Introduction State management is like the navigation system on the Voyager spacecraft β when everything's working smoothly, you barely notice it's there. But when things go wrong, it becomes the difference between reaching interstellar space and drifting lost in the cosmic void. In web development, state represents everything your application needs to remember: user login status, form data, navigation history, and temporary interface states. As your banking app has evolved from a simple login form into a more sophisticated application, you've likely encountered some common challenges. Refresh the page and users get logged out unexpectedly. Close the browser and all progress disappears. Debug a problem and you're hunting through multiple functions that all modify the same data in different ways. These aren't signs of poor coding β they're the natural growing pains that occur when applications reach a certain complexity threshold. Every developer faces these challenges as their apps transition from "proof of concept" to "production ready." In this lesson, we'll implement a centralized state management system that transforms your banking app into a reliable, professional application. You'll learn to manage data flows predictably, persist user sessions appropriately, and create the smooth user experience that modern web applications require. ## Prerequisites Before diving into state management concepts, you'll need to have your development environment properly set up and your banking app foundation in place. This lesson builds directly on the concepts and code from previous parts of this series. Make sure you have the following components ready before proceeding: Required Setup: - Complete the data fetching lesson - your app should successfully load and display account data - Install Node.js on your system for running the backend API - Start the server API locally to handle account data operations Testing Your Environment: Verify that your API server is running correctly by executing this command in a terminal: What this command does: - Sends a GET request to your local API server - Tests the connection and verifies the server is responding - Returns the API version information if everything is working correctly ## π§ State Management Architecture Overview Core Principle: Professional state management balances predictability, persistence, and performance to create reliable user experiences that scale from simple interactions to complex application workflows. --- ## Diagnosing the Current State Issues Like Sherlock Holmes examining a crime scene, we need to understand exactly what's happening in our current implementation before we can solve the mystery of disappearing user sessions. Let's conduct a simple experiment that reveals the underlying state management challenges: π§ͺ Try This Diagnostic Test: 1. Log into your banking app and navigate to the dashboard 2. Refresh the browser page 3. Observe what happens to your login status If you're redirected back to the login screen, you've discovered the classic state persistence problem. This behavior occurs because our current implementation stores user data in JavaScript variables that reset with each page load. Current Implementation Problems: The simple account variable from our previous lesson creates three significant issues that affect both user experience and code maintainability: The Architectural Challenge: Like the Titanic's compartmentalized design that seemed robust until multiple compartments flooded simultaneously, fixing these issues individually won't address the underlying architectural problem. We need a comprehensive state management solution. State management is really about solving two fundamental puzzles: 1. Where's My Data?: Keeping track of what information we have and where it's coming from 2. Is Everyone on the Same Page?: Making sure what users see matches what's actually happening Our Game Plan: Instead of chasing our tails, we're going to create a centralized state management system. Think of it like having one really organized person in charge of all the important stuff: Understanding this data flow: - Centralizes all application state in one location - Routes all state changes through controlled functions - Ensures the UI stays synchronized with the current state - Provides a clear, predictable pattern for data management ### Task: Centralize State Structure Let's begin transforming our scattered state management into a centralized system. This first step establishes the foundation for all the improvements that follow. Step 1: Create a Central State Object Replace the simple account declaration: With a structured state object: Here's why this change matters: - Centralizes all application data in one location - Prepares the structure for adding more state properties later - Creates a clear boundary between state and other variables - Establishes a pattern that scales as your app grows Step 2: Update State Access Patterns Update your functions to use the new state structure: In register() and login() functions, replace: With: In updateDashboard() function, add this line at the top: What these updates accomplish: - Maintains existing functionality while improving structure - Prepares your code for more sophisticated state management - Creates consistent patterns for accessing state data - Establishes the foundation for centralized state updates ### π― Pedagogical Check-in: Centralization Principles Pause and Reflect: You've just implemented the foundation of centralized state management. This is a crucial architectural decision. Quick Self-Assessment: - Can you explain why centralizing state in one object is better than scattered variables? - What would happen if you forgot to update a function to use state.account? - How does this pattern prepare your code for more advanced features? Real-World Connection: The centralization pattern you've learned is the foundation of modern frameworks like Redux, Vuex, and React Context. You're building the same architectural thinking used in major applications. Challenge Question: If you needed to add user preferences (theme, language) to your app, where would you add them in the state structure? How would this scale? ## Implementing Controlled State Updates With our state centralized, the next step involves establishing controlled mechanisms for data modifications. This approach ensures predictable state changes and easier debugging. The core principle resembles air traffic control: instead of allowing multiple functions to modify state independently, we'll channel all changes through a single, controlled function. This pattern provides clear oversight of when and how data changes occur. Immutable State Management: We'll treat our state object as immutable, meaning we never modify it directly. Instead, each change creates a new state object with the updated data. While this approach might initially seem inefficient compared to direct modifications, it provides significant advantages for debugging, testing, and maintaining application predictability. Benefits of immutable state management: JavaScript Immutability with Object.freeze(): JavaScript provides Object.freeze() to prevent object modifications: Breaking down what happens here: - Prevents direct property assignments or deletions - Throws exceptions if modification attempts are made - Ensures state changes must go through controlled functions - Creates a clear contract for how state can be updated ### Task Let's create a new updateState() function: In this function, we're creating a new state object and copy data from the previous state using the spread (...) operator. Then we override a particular property of the state object with the new data using the bracket notation [property] for assignment. Finally, we lock the object to prevent modifications using Object.freeze(). We only have the account property stored in the state for now, but with this approach you can add as many properties as you need in the state. We'll also update the state initialization to make sure the initial state is frozen too: After that, update the register function by replacing the state.account = result; assignment with: Do the same with the login function, replacing state.account = data; with: We'll now take the chance to fix the issue of account data not being cleared when the user clicks on Logout. Create a new function logout(): In updateDashboard(), replace the redirection return navigate('/login'); with return logout(); Try registering a new account, logging out and in again to check that everything still works correctly. ## Implementing Data Persistence The session loss issue we identified earlier requires a persistence solution that maintains user state across browser sessions. This transforms our application from a temporary experience into a reliable, professional tool. Consider how atomic clocks maintain precise time even through power outages by storing critical state in non-volatile memory. Similarly, web applications need persistent storage mechanisms to preserve essential user data across browser sessions and page refreshes. Strategic Questions for Data Persistence: Before implementing persistence, consider these critical factors: Browser Storage Options: Modern browsers provide several storage mechanisms, each designed for different use cases: Primary Storage APIs: 1. localStorage: Persistent Key/Value storage - Persists data across browser sessions indefinitely - Survives browser restarts and computer reboots - Scoped to the specific website domain - Perfect for user preferences and login states 2. sessionStorage: Temporary session storage - Functions identically to localStorage during active sessions - Clears automatically when the browser tab closes - Ideal for temporary data that shouldn't persist 3. HTTP Cookies: Server-shared storage - Automatically sent with every server request - Perfect for authentication tokens - Limited in size and can impact performance Data Serialization Requirement: Both localStorage and sessionStorage only store strings: Understanding serialization: - Converts JavaScript objects to JSON strings using JSON.stringify() - Reconstructs objects from JSON using JSON.parse() - Handles complex nested objects and arrays automatically - Fails on functions, undefined values, and circular references ### Task: Implement localStorage Persistence Let's implement persistent storage so users stay logged in until they explicitly log out. We'll use localStorage to store account data across browser sessions. Step 1: Define Storage Configuration What this constant provides: - Creates a consistent identifier for our stored data - Prevents typos in storage key references - Makes it easy to change the storage key if needed - Follows best practices for maintainable code Step 2: Add Automatic Persistence Add this line at the end of the updateState() function: Breaking down what happens here: - Converts the account object to a JSON string for storage - Saves the data using our consistent storage key - Executes automatically whenever state changes occur - Ensures stored data is always synchronized with current state Step 3: Restore State on App Load Create an initialization function to restore saved data: Understanding the initialization process: - Retrieves any previously saved account data from localStorage - Parses the JSON string back into a JavaScript object - Updates the state using our controlled update function - Restores the user's session automatically on page load - Executes before route updates to ensure state is available Step 4: Optimize Default Route Update the default route to take advantage of persistence: In updateRoute(), replace: Why this change makes sense: - Leverages our new persistence system effectively - Allows the dashboard to handle authentication checks - Redirects to login automatically if no saved session exists - Creates a more seamless user experience Testing Your Implementation: 1. Log into your banking app 2. Refresh the browser page 3. Verify you remain logged in and on the dashboard 4. Close and reopen your browser 5. Navigate back to your app and confirm you're still logged in π Achievement Unlocked: You've successfully implemented persistent state management! Your app now behaves like a professional web application. ### π― Pedagogical Check-in: Persistence Architecture Architecture Understanding: You've implemented a sophisticated persistence layer that balances user experience with data management complexity. Key Concepts Mastered: - JSON Serialization: Converting complex objects to storable strings - Automatic Synchronization: State changes trigger persistent storage - Session Recovery: Apps can restore user context after interruptions - Centralized Persistence: One update function handles all storage Industry Connection: This persistence pattern is fundamental to Progressive Web Apps (PWAs), offline-first applications, and modern mobile web experiences. You're building production-level capabilities. Reflection Question: How would you modify this system to handle multiple user accounts on the same device? Consider privacy and security implications. ## Balancing Persistence with Data Freshness Our persistence system successfully maintains user sessions, but introduces a new challenge: data staleness. When multiple users or applications modify the same server data, local cached information becomes outdated. This situation resembles Viking navigators who relied on both stored star charts and current celestial observations. The charts provided consistency, but navigators needed fresh observations to account for changing conditions. Similarly, our application needs both persistent user state and current server data. π§ͺ Discovering the Data Freshness Problem: 1. Log into the dashboard using the test account 2. Run this command in a terminal to simulate a transaction from another source: 3. Refresh your dashboard page in the browser 4. Observe whether you see the new transaction What this test demonstrates: - Shows how local storage can become "stale" (outdated) - Simulates real-world scenarios where data changes occur outside your app - Reveals the tension between persistence and data freshness The Data Staleness Challenge: Solution Strategy: We'll implement a "refresh on load" pattern that balances the benefits of persistence with the need for fresh data. This approach maintains the smooth user experience while ensuring data accuracy. ### Task: Implement Data Refresh System We'll create a system that automatically fetches fresh data from the server while maintaining the benefits of our persistent state management. Step 1: Create Account Data Updater Understanding this function's logic: - Checks if a user is currently logged in (state.account exists) - Redirects to logout if no valid session is found - Fetches fresh account data from the server using the existing getAccount() function - Handles server errors gracefully by logging out invalid sessions - Updates the state with fresh data using our controlled update system - Triggers automatic localStorage persistence through the updateState() function Step 2: Create Dashboard Refresh Handler What this refresh function accomplishes: - Coordinates the data refresh and UI update process - Waits for fresh data to be loaded before updating the display - Ensures the dashboard shows the most current information - Maintains a clean separation between data management and UI updates Step 3: Integrate with Route System Update your route configuration to trigger refresh automatically: How this integration works: - Executes the refresh function every time the dashboard route loads - Ensures fresh data is always displayed when users navigate to the dashboard - Maintains the existing route structure while adding data freshness - Provides a consistent pattern for route-specific initialization Testing Your Data Refresh System: 1. Log into your banking app 2. Run the curl command from earlier to create a new transaction 3. Refresh your dashboard page or navigate away and back 4. Verify that the new transaction appears immediately π Perfect Balance Achieved: Your app now combines the smooth experience of persistent state with the accuracy of fresh server data! ## π Your State Management Mastery Timeline π Graduation Milestone: You've successfully built a complete state management system using the same principles that power Redux, Vuex, and other professional state libraries. These patterns scale from simple apps to enterprise applications. π Next Level Capabilities: - Ready to master state management frameworks (Redux, Zustand, Pinia) - Prepared to implement real-time features with WebSockets - Equipped to build offline-first Progressive Web Apps - Foundation set for advanced patterns like state machines and observers ## GitHub Copilot Agent Challenge π Use the Agent mode to complete the following challenge: Description: Implement a comprehensive state management system with undo/redo functionality for the banking app. This challenge will help you practice advanced state management concepts including state history tracking, immutable updates, and user interface synchronization. Prompt: Create an enhanced state management system that includes: 1) A state history array that tracks all previous states, 2) Undo and redo functions that can revert to previous states, 3) UI buttons for undo/redo operations on the dashboard, 4) A maximum history limit of 10 states to prevent memory issues, and 5) Proper cleanup of history when the user logs out. Ensure the undo/redo functionality works with account balance changes and persists across browser refreshes. Learn more about agent mode here. ## π Challenge: Storage Optimization Your implementation now handles user sessions, data refresh, and state management effectively. However, consider whether our current approach optimally balances storage efficiency with functionality. Like chess masters who distinguish between essential pieces and expendable pawns, effective state management requires identifying which data must persist versus which should always be fresh from the server. Optimization Analysis: Evaluate your current localStorage implementation and consider these strategic questions: - What's the minimum information required to maintain user authentication? - Which data changes frequently enough that local caching provides little benefit? - How can storage optimization improve performance without degrading user experience? This type of architectural analysis distinguishes experienced developers who consider both functionality and efficiency in their solutions. Implementation Strategy: - Identify the essential data that must persist (likely just user identification) - Modify your localStorage implementation to store only critical session data - Ensure fresh data is always loaded from the server on dashboard visits - Test that your optimized approach maintains the same user experience Advanced Consideration: - Compare the trade-offs between storing full account data vs. just authentication tokens - Document your decisions and reasoning for future team members This challenge will help you think like a professional developer who considers both user experience and application efficiency. Take your time to experiment with different approaches! ## Post-Lecture Quiz Post-lecture quiz ## Assignment Implement "Add transaction" dialog Here's an example result after completing the assignment:
Bank API
The API is already built for you and not part of the exercise. However, if you're interested to learn how to build an API like this you can follow this series of videos: https://aka.ms/NodeBeginner (videos 17 through 21 covers this exact API). You can also take a look at this interactive tutorial: https://aka.ms/learn/express-api ## Running the server Make sure you have Node.js installed. 1. Git clone this repo The Web-Dev-For-Beginners. 2. Open your terminal and navigate into the Web-Dev-For-Beginners/7-bank-project/api folder 2. Run npm install and wait for the packages to be installed(could take a while depending on the quality of your internet connection). 3. When the installation is over, run npm start and you are good to go. The server should start listening on port 5000. This server will be running together with the main bank app server terminal(listening on port 3000), do not close it. ## API details Route | Description ---------------------------------------------|------------------------------------ GET /api/ | Get server info POST /api/accounts/ | Create an account, ex: { user: 'Yohan', description: 'My budget', currency: 'EUR', balance: 100 } GET /api/accounts/:user | Get all data for the specified account DELETE /api/accounts/:user | Remove specified account POST /api/accounts/:user/transactions | Add a transaction, ex: { date: '2020-07-23T18:25:43.511Z', object: 'Bought a book', amount: -20 } DELETE /api/accounts/:user/transactions/:id | Remove specified transaction
Bank app
## Running the app First make sure you have the API server running. Any web server can be used to run the app, but since you should have Node.js installed anyway to run the API, you can: 1. Git clone this repo. 2. Open a terminal, navigate to this directory, then run npx lite-server .. It will start a development web server on port 3000 3. Open http://localhost:3000 in a browser to run the app.