452 lines
No EOL
14 KiB
Markdown
452 lines
No EOL
14 KiB
Markdown
# MVC Webshop Application
|
|
|
|
A pure JavaScript implementation of a webshop using the Model-View-Controller (MVC) architectural pattern. This educational project demonstrates core web development concepts without external frameworks.
|
|
|
|
## 🏗️ Architecture Overview
|
|
|
|
This application follows the MVC pattern with a centralized view rendering system:
|
|
|
|
- **Model**: Handles data management and business logic
|
|
- **View**: Manages HTML generation and DOM manipulation
|
|
- **Controller**: Coordinates user interactions and data flow
|
|
|
|
## 📊 UML Diagrams
|
|
|
|
### Class Diagram
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class Model {
|
|
-products: Array~Product~
|
|
-cart: Array~CartItem~
|
|
+getAllProducts() Array~Product~
|
|
+getProductById(id) Product
|
|
+addToCart(productId, quantity) boolean
|
|
+removeFromCart(productId) void
|
|
+updateCartQuantity(productId, quantity) void
|
|
+getCart() Array~CartItem~
|
|
+getCartTotal() number
|
|
+getCartItemCount() number
|
|
+clearCart() void
|
|
+saveCartToStorage() void
|
|
+loadCartFromStorage() Array~CartItem~
|
|
+initializeCart() void
|
|
}
|
|
|
|
class View {
|
|
-currentPage: string
|
|
-cartCount: number
|
|
+updateView(viewType, data) void
|
|
+generateNavbar() string
|
|
+generateProductsPage(products) string
|
|
+generateProductDetailPage(product) string
|
|
+generateCartPage(cartItems, total, itemCount) string
|
|
+createProductCard(product) string
|
|
+createCartItem(item) string
|
|
+attachEventListeners() void
|
|
+updateCartCountInNav() void
|
|
+showNotification(message) void
|
|
}
|
|
|
|
class Controller {
|
|
+loadProducts() void
|
|
+showProductDetail(productId) void
|
|
+handleAddToCart(productId) void
|
|
+handleRemoveFromCart(productId) void
|
|
+changeQuantity(productId, change) void
|
|
+handleClearCart() void
|
|
+navigateToProducts() void
|
|
+navigateToCart() void
|
|
+updateCartDisplay() void
|
|
}
|
|
|
|
class Product {
|
|
+id: number
|
|
+name: string
|
|
+price: number
|
|
+description: string
|
|
+image: string
|
|
+category: string
|
|
}
|
|
|
|
class CartItem {
|
|
+productId: number
|
|
+quantity: number
|
|
+product: Product
|
|
}
|
|
|
|
class App {
|
|
+initialize() void
|
|
}
|
|
|
|
Controller --> Model : uses
|
|
Controller --> View : updates
|
|
View --> Controller : sends events
|
|
Model --> Product : contains
|
|
Model --> CartItem : manages
|
|
CartItem --> Product : references
|
|
App --> Controller : initializes
|
|
App --> Model : sets up
|
|
App --> View : starts
|
|
```
|
|
|
|
### Sequence Diagram - Add to Cart Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant U as User
|
|
participant V as View
|
|
participant C as Controller
|
|
participant M as Model
|
|
|
|
U->>V: Click "Add to Cart"
|
|
V->>C: handleAddToCart(productId)
|
|
C->>M: addToCart(productId)
|
|
M->>M: getProductById(productId)
|
|
M->>M: Find existing item or create new
|
|
M->>M: saveCartToStorage()
|
|
M-->>C: return true/false
|
|
|
|
alt Success
|
|
C->>M: getProductById(productId)
|
|
M-->>C: return product
|
|
C->>V: updateView('notification', {message})
|
|
C->>C: updateCartDisplay()
|
|
C->>M: getCart()
|
|
M-->>C: return cart
|
|
C->>M: getCartTotal()
|
|
M-->>C: return total
|
|
C->>M: getCartItemCount()
|
|
M-->>C: return itemCount
|
|
C->>V: updateView('cart', {cartItems, total, itemCount})
|
|
C->>V: updateView('cart-count', {count})
|
|
V->>V: generateCartPage() or updateCartCountInNav()
|
|
V-->>U: Updated UI with notification
|
|
else Failure
|
|
C->>V: updateView('notification', {message: 'Error'})
|
|
V-->>U: Error message
|
|
end
|
|
```
|
|
|
|
## 📁 Project Structure
|
|
|
|
```
|
|
mvc-emne2/
|
|
├── index.html # Main HTML file with single app container
|
|
├── styles.css # Application styling
|
|
├── js/
|
|
│ ├── app.js # Application initialization
|
|
│ ├── model.js # Data layer and business logic
|
|
│ ├── view.js # UI rendering and HTML generation
|
|
│ └── controller.js # User interaction handling
|
|
├── flow-diagram.md # Application flow visualization
|
|
└── README.md # This documentation
|
|
```
|
|
|
|
## 🔧 Key Components
|
|
|
|
### 1. Model (`model.js`)
|
|
|
|
**Purpose**: Manages application data and persistence
|
|
|
|
**Key Functions**:
|
|
- `getAllProducts()` - Returns product catalog
|
|
- `getProductById(id)` - Retrieves specific product
|
|
- `addToCart(productId, quantity)` - Adds items to cart
|
|
- `removeFromCart(productId)` - Removes items from cart
|
|
- `updateCartQuantity(productId, quantity)` - Updates item quantities
|
|
- `getCart()` - Returns current cart contents
|
|
- `getCartTotal()` - Calculates cart total price
|
|
- `getCartItemCount()` - Returns total item count
|
|
- `clearCart()` - Empties the cart
|
|
- `saveCartToStorage()` / `loadCartFromStorage()` - LocalStorage persistence
|
|
|
|
**Data Structure**:
|
|
```javascript
|
|
// Product
|
|
{
|
|
id: number,
|
|
name: string,
|
|
price: number,
|
|
description: string,
|
|
image: string (emoji),
|
|
category: string
|
|
}
|
|
|
|
// Cart Item
|
|
{
|
|
productId: number,
|
|
quantity: number,
|
|
product: Product
|
|
}
|
|
```
|
|
|
|
### 2. View (`view.js`)
|
|
|
|
**Purpose**: Handles all UI rendering through a centralized `updateView` function
|
|
|
|
**Core Architecture**:
|
|
```javascript
|
|
function updateView(viewType, data = {}) {
|
|
switch (viewType) {
|
|
case 'products': // Render product listing
|
|
case 'product-detail': // Render single product
|
|
case 'cart': // Render shopping cart
|
|
case 'cart-count': // Update cart badge
|
|
case 'notification': // Show user messages
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key Features**:
|
|
- **Single Entry Point**: All rendering goes through `updateView()`
|
|
- **Complete Page Generation**: Generates entire page HTML including navigation
|
|
- **Event Listener Management**: Automatically attaches event listeners after each render
|
|
- **Template Functions**: Modular HTML generation functions
|
|
|
|
**HTML Generation Functions**:
|
|
- `generateNavbar()` - Creates navigation header
|
|
- `generateProductsPage(products)` - Product listing page
|
|
- `generateProductDetailPage(product)` - Product detail page
|
|
- `generateCartPage(cartItems, total, itemCount)` - Shopping cart page
|
|
- `createProductCard(product)` - Individual product card
|
|
- `createCartItem(item)` - Individual cart item
|
|
|
|
### 3. Controller (`controller.js`)
|
|
|
|
**Purpose**: Handles user interactions and coordinates between Model and View
|
|
|
|
**Navigation Functions**:
|
|
- `navigateToProducts()` - Shows product listing
|
|
- `navigateToCart()` - Shows shopping cart
|
|
- `showProductDetail(productId)` - Shows product details
|
|
|
|
**Cart Management**:
|
|
- `handleAddToCart(productId)` - Adds product to cart
|
|
- `handleRemoveFromCart(productId)` - Removes product from cart
|
|
- `changeQuantity(productId, change)` - Updates product quantity
|
|
- `handleClearCart()` - Empties entire cart
|
|
- `updateCartDisplay()` - Refreshes cart view and counter
|
|
|
|
**Key Design Patterns**:
|
|
- All functions call `updateView()` for UI updates
|
|
- User notifications are handled consistently
|
|
- Cart state is automatically persisted after changes
|
|
|
|
### 4. Application Initialization (`app.js`)
|
|
|
|
**Purpose**: Bootstraps the application
|
|
|
|
**Initialization Flow**:
|
|
1. Wait for DOM to load
|
|
2. Initialize cart from localStorage
|
|
3. Load products from model
|
|
4. Render initial products page
|
|
5. Update cart counter
|
|
|
|
```javascript
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initializeCart();
|
|
|
|
const products = getAllProducts();
|
|
const itemCount = getCartItemCount();
|
|
|
|
updateView('products', { products });
|
|
updateView('cart-count', { count: itemCount });
|
|
|
|
console.log('Nettbutikk MVC Demo initialisert!');
|
|
});
|
|
```
|
|
|
|
## 🔄 Data Flow
|
|
|
|
### 1. Application Startup
|
|
```
|
|
HTML Load → DOMContentLoaded → initializeCart() → loadCartFromStorage() →
|
|
getAllProducts() → updateView('products') → generateProductsPage() → attachEventListeners()
|
|
```
|
|
|
|
### 2. User Interactions
|
|
```
|
|
User Click → Controller Function → Model Operation → updateView() →
|
|
HTML Generation → Event Listener Attachment → UI Update
|
|
```
|
|
|
|
### 3. Cart Operations
|
|
```
|
|
Add to Cart → addToCart() → saveCartToStorage() → updateCartDisplay() →
|
|
updateView('cart') + updateView('cart-count') → showNotification()
|
|
```
|
|
|
|
## 🎨 View Rendering System
|
|
|
|
### Centralized Rendering
|
|
All UI updates flow through the single `updateView()` function:
|
|
|
|
```javascript
|
|
updateView('products', { products: [...] });
|
|
updateView('product-detail', { product: {...} });
|
|
updateView('cart', { cartItems: [...], total: 0, itemCount: 0 });
|
|
updateView('cart-count', { count: 0 });
|
|
updateView('notification', { message: 'Success!' });
|
|
```
|
|
|
|
### Complete Page Generation
|
|
Unlike traditional DOM manipulation, this system generates complete page HTML:
|
|
|
|
```javascript
|
|
function generateProductsPage(products) {
|
|
return `
|
|
${generateNavbar()}
|
|
<main>
|
|
<section class="page active">
|
|
<h2>Våre Produkter</h2>
|
|
<div class="products-grid">
|
|
${products.map(product => createProductCard(product)).join('')}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
`;
|
|
}
|
|
```
|
|
|
|
### Event Listener Management
|
|
After each render, event listeners are reattached:
|
|
|
|
```javascript
|
|
function attachEventListeners() {
|
|
const productsLink = document.querySelector('[data-page="products"]');
|
|
const cartLink = document.querySelector('[data-page="cart"]');
|
|
|
|
if (productsLink) {
|
|
productsLink.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
navigateToProducts();
|
|
});
|
|
}
|
|
// ... more listeners
|
|
}
|
|
```
|
|
|
|
## 💾 Data Persistence
|
|
|
|
### LocalStorage Integration
|
|
Cart data is automatically persisted:
|
|
|
|
```javascript
|
|
function saveCartToStorage() {
|
|
try {
|
|
localStorage.setItem('webstore_cart', JSON.stringify(cart));
|
|
} catch (error) {
|
|
console.error('Feil ved lagring av handlekurv til localStorage:', error);
|
|
}
|
|
}
|
|
|
|
function loadCartFromStorage() {
|
|
try {
|
|
const savedCart = localStorage.getItem('webstore_cart');
|
|
if (savedCart) {
|
|
const parsedCart = JSON.parse(savedCart);
|
|
return parsedCart.map(item => ({
|
|
...item,
|
|
product: products.find(p => p.id === item.productId) ||
|
|
{ id: item.productId, name: 'Ukjent Produkt', price: 0 }
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
console.error('Feil ved lasting av handlekurv fra localStorage:', error);
|
|
}
|
|
return [];
|
|
}
|
|
```
|
|
|
|
## 🚀 Usage
|
|
|
|
### Running the Application
|
|
1. Open `index.html` in a web browser
|
|
2. The application initializes automatically
|
|
3. Navigate between products, product details, and cart
|
|
4. Cart state persists across browser sessions
|
|
|
|
### Key Features
|
|
- **Product Browsing**: View all products in a grid layout
|
|
- **Product Details**: Click any product for detailed view
|
|
- **Shopping Cart**: Add, remove, and modify quantities
|
|
- **Persistent Cart**: Cart contents saved in localStorage
|
|
- **Responsive Design**: Works on desktop and mobile
|
|
- **User Notifications**: Feedback for all user actions
|
|
|
|
## 🎯 Educational Benefits
|
|
|
|
### MVC Pattern Demonstration
|
|
- Clear separation of concerns
|
|
- Model handles data, View handles presentation, Controller handles logic
|
|
- Demonstrates how components interact in MVC architecture
|
|
|
|
### Modern JavaScript Features
|
|
- ES6+ syntax (arrow functions, template literals, destructuring)
|
|
- LocalStorage API usage
|
|
- Event handling patterns
|
|
- Functional programming concepts
|
|
|
|
### Web Development Fundamentals
|
|
- DOM manipulation techniques
|
|
- Event listener management
|
|
- Client-side state management
|
|
- HTML generation and templating
|
|
- CSS styling integration
|
|
|
|
## 🔧 Technical Decisions
|
|
|
|
### Why Centralized Rendering?
|
|
- **Consistency**: All UI updates follow the same pattern
|
|
- **Maintainability**: Easy to track and debug UI changes
|
|
- **Scalability**: Simple to add new view types
|
|
- **State Management**: Clear data flow from model to view
|
|
|
|
### Why Complete Page Generation?
|
|
- **Simplicity**: No complex DOM diffing or state tracking
|
|
- **Reliability**: Fresh render eliminates stale state issues
|
|
- **Performance**: Modern browsers handle innerHTML efficiently
|
|
- **Debugging**: Easy to see exactly what HTML is generated
|
|
|
|
### Why LocalStorage?
|
|
- **Persistence**: Cart survives browser restarts
|
|
- **Simplicity**: No server-side complexity
|
|
- **Performance**: Instant load times
|
|
- **Educational**: Demonstrates client-side storage concepts
|
|
|
|
## 🎨 Styling Architecture
|
|
|
|
The application uses a clean, modern CSS design:
|
|
- Responsive grid layouts
|
|
- Card-based product display
|
|
- Consistent button and form styling
|
|
- Mobile-friendly navigation
|
|
- Smooth transitions and hover effects
|
|
|
|
## 🔮 Future Enhancements
|
|
|
|
This MVC implementation serves as a foundation for progressive enhancement:
|
|
|
|
1. **Phase 2**: Add TypeScript for type safety
|
|
2. **Phase 3**: Implement Vue.js for reactive UI
|
|
3. **Phase 4**: Add user authentication
|
|
4. **Phase 5**: Integrate payment processing
|
|
5. **Phase 6**: Add product search and filtering
|
|
6. **Phase 7**: Implement real backend API
|
|
|
|
## 📚 Learning Outcomes
|
|
|
|
By studying this codebase, you'll understand:
|
|
- MVC architectural pattern implementation
|
|
- Client-side state management techniques
|
|
- Modern JavaScript development practices
|
|
- HTML generation and templating approaches
|
|
- Event-driven programming patterns
|
|
- LocalStorage API usage
|
|
- Responsive web design principles
|
|
|
|
---
|
|
|
|
*This project is part of the GET Frontend Development curriculum, demonstrating core web development concepts through practical implementation.* |