Skip to content

Latest commit

 

History

History
317 lines (241 loc) · 9.29 KB

File metadata and controls

317 lines (241 loc) · 9.29 KB

Dev-Only Examples with jsgui3-server

This directory contains examples that demonstrate client-side functionality using jsgui3-server for server-side rendering and development. These examples are specifically designed to show how jsgui3-html works in an isomorphic (server + client) context.

Why Dev-Only Examples?

These examples require jsgui3-server which is listed as a devDependency only. This means:

  • They demonstrate client-side capabilities in a realistic server context
  • They showcase server-side rendering + client-side activation patterns
  • They don't add jsgui3-server to production dependencies
  • The main examples/ directory contains standalone examples that don't require a server

Prerequisites

  1. Install all dependencies including devDependencies:
npm install
  1. The jsgui3-server package will be installed automatically

Running Examples

Each example has its own server script. To run an example:

# From the jsgui3-html root directory
node dev-examples/binding/counter/server.js

# Or from within the example directory
cd dev-examples/binding/counter
node server.js

Then open your browser to http://localhost:52000 (or the port shown in console).

Example Categories

Binding Examples (binding/)

Demonstrate MVVM data binding with server-side rendering:

  • counter/ - Simple counter with server-side rendering and client-side activation
  • user-form/ - Form with server-side validation API
  • missing-controls/ - New core controls demo (inputs, indicators, navigation, feedback)
  • data-controls/ - Data table, data grid, virtualization, and collection controls
  • layout-controls/ - Layout and navigation controls (split pane, tabs, drawer, stepper)
  • form_editor_features/ - Form container, validation, input masks, autosize, rich text, object editor
  • showcase_app/ - Polished control showcase with live Theme Studio (preset + token editor)

Progressive Enhancement (progressive/)

Demonstrates Activation tiers for native controls:

  • progressive/ - Tier 0 (native), Tier 1 (styled), Tier 2 (activated), and mixed activation markers

Key Patterns Demonstrated

1. Server-Side Rendering + Client-Side Activation

// server.js
const Server = require('jsgui3-server');
const jsgui = require('./client');

const server = new Server({
    Ctrl: jsgui.controls.Demo_UI,
    src_path_client_js: require.resolve('./client.js')
});

server.on('ready', () => {
    server.start(52000, (err) => {
        if (err) throw err;
        console.log('Server started on port 52000');
    });
});

The server:

  1. Renders the control to HTML on the server
  2. Bundles the client-side JavaScript
  3. Sends HTML with embedded data to the client
  4. Client JavaScript activates the DOM, attaching event handlers

2. Server-Side API with server.publish()

// server.js
server.on('ready', () => {
    // Publish API endpoints
    server.publish('validateEmail', async (email) => {
        // Returns JSON automatically
        return { 
            valid: /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) 
        };
    });
    
    server.start(52000);
});
// client.js - calling the API
const response = await fetch('/api/validateEmail', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email: 'test@example.com' })
});
const result = await response.json();

3. Isomorphic Control Definition

The same control code runs on both server and client:

// client.js - defines the UI
const jsgui = require('jsgui3-html');
const Active_HTML_Document = jsgui.Active_HTML_Document;

class Demo_UI extends Active_HTML_Document {
    constructor(spec = {}) {
        super(spec);
        const { context } = this;
        
        // This code runs on BOTH server and client
        // Use data binding to keep state synchronized
    }
    
    activate() {
        // This code ONLY runs on the client
        // Set up client-side event handlers here
    }
}

module.exports = jsgui;

4. Data Binding with Server Context

// The binding system works seamlessly across server/client boundary
this.bind('count', model, {
    toView: (value) => `Count: ${value}`,
    toModel: (text) => parseInt(text) || 0
});

// On server: Renders initial value into HTML
// On client: Updates DOM when model changes

Differences from Standalone Examples

Aspect Standalone Examples (examples/) Dev Examples (dev-examples/)
Dependencies None (plain Node.js) Requires jsgui3-server
Execution node examples/binding_simple_counter.js node dev-examples/binding/counter/server.js
Purpose Show binding system API Show isomorphic patterns
Rendering Client-side only Server-side + client-side
File Structure Single file client.js + server.js
Browser Required No Yes
Real-world Pattern API demonstration Production-like setup

Development Workflow

  1. Start the server - Run the example's server script
  2. Open browser - Navigate to the displayed URL
  3. Make changes - Edit client.js for UI changes
  4. Restart server - Server rebuilds client bundle automatically
  5. Refresh browser - See your changes

Server Configuration Options

const server = new Server({
    Ctrl: Demo_UI,                          // Main UI control
    src_path_client_js: 'path/to/client',  // Client entry point
    debug: true,                            // Include source maps
    port: 52000,                            // Custom port (optional)
});

API Publishing

The server can publish functions as HTTP endpoints:

server.on('ready', () => {
    // Simple function - returns text/plain
    server.publish('hello', () => 'Hello, World!');
    
    // Complex function - returns application/json
    server.publish('getData', () => {
        return { users: ['Alice', 'Bob'], count: 2 };
    });
    
    // Async function
    server.publish('fetchUser', async (userId) => {
        const user = await database.getUser(userId);
        return user;
    });
    
    server.start(52000);
});

All published functions are available at /api/<name>.

Client-Side Control Activation

Controls have two phases:

  1. Constructor - Runs on both server and client

    • Define UI structure
    • Set up data binding
    • Configure initial state
  2. Activate - Runs only on client

    • Attach event handlers
    • Start animations
    • Make API calls
class MyControl extends Active_HTML_Document {
    constructor(spec = {}) {
        super(spec);
        // Runs on server AND client
    }
    
    activate() {
        if (!this.__active) {
            super.activate();
            // Only runs on client
        }
    }
}

CSS Handling

CSS is automatically extracted from control definitions:

Demo_UI.css = `
    .demo-ui {
        padding: 20px;
        background: #f5f5f5;
    }
    
    .counter-value {
        font-size: 2em;
        font-weight: bold;
    }
`;

The server includes this CSS in the served HTML automatically.

Debugging

Server-Side Debugging

node --inspect dev-examples/binding/counter/server.js

Client-Side Debugging

Use browser DevTools. Enable source maps with debug: true in server config.

Common Issues

  1. Port already in use: Change the port number in server.js
  2. Module not found: Run npm install from project root
  3. Changes not reflected: Restart the server (it rebuilds on startup)

Example Structure

Each example follows this structure:

example-name/
├── client.js       # UI control definition (runs on both server and client)
├── server.js       # Server setup and API endpoints
└── README.md       # Example-specific documentation

Best Practices

  1. Separate concerns: Keep server logic in server.js, UI in client.js
  2. Use activation properly: Constructor for structure, activate for interaction
  3. Leverage binding: Let the binding system handle DOM updates
  4. Publish APIs: Use server.publish() for backend functionality
  5. Handle errors: Both server and client should handle errors gracefully

Learning Path

  1. Start with counter - Simplest example of server-side rendering
  2. Move to user-form - Adds server validation
  3. Explore data-grid - Shows data loading and client-side operations
  4. Study master-detail - Demonstrates navigation patterns

Additional Resources

Contributing

When adding new dev examples:

  1. Follow the client.js + server.js structure
  2. Include a README.md explaining the example
  3. Demonstrate a specific pattern or feature
  4. Keep examples focused and well-commented
  5. Test that server-side rendering works correctly

Questions?