A web component framework for building interactive web applications in Pharo Smalltalk. WildCamping leverages PharoJS to transpile Smalltalk code to JavaScript, enabling you to write full-stack web applications entirely in Pharo.
WildCamping is a component-oriented framework that bridges the gap between server-side Smalltalk logic and client-side web interfaces. It provides an elegant, object-oriented approach to building interactive web applications with automatic JavaScript generation and encapsulated component architecture.
- Object-Oriented HTML Generation: Build dynamic and static HTML using a canvas-based API with full OO encapsulation
- Automatic JavaScript Generation: Write your component logic in Pharo and automatically generate optimized JavaScript for browser execution
- Reusable Components: Encapsulated, composable components with clear separation of concerns and lifecycle management
- Dynamic Updates: Support for interactive components that respond to user events and update dynamically without page reloads
- WebWorker Integration: Leverage browser WebWorkers via the Job interface for offloading compute-intensive tasks (WIP)
The framework centers around the WCComponent base class. Each component goes through three key phases:
- Instance Setup (
initialize): Initialize your component's instance variables, models, and state - HTML Structure (
renderHtmlOn:): Define your component's HTML template using the canvas-based tag brush API - Event Initialization (
start): Set up callbacks, event handlers, and initialize dynamic behavior
Example:
WCCHelloWorld >> renderHtmlOn: html
html div
id: 'hello';
with: 'Hello, World!'
WCCHelloWorld >> start
"Initialize callbacks and event handlers here"- WCGeohashWebApp: A GeoHash implementation demonstrating spatial indexing
- UBNameGenerator: An Ubuntu-like name generator showcasing dynamic component interaction
WildCamping provides an abstraction layer over HTML and DOM manipulation:
- Tag Brush Canvas API: Fluent, object-oriented interface for building HTML with method chaining (e.g.,
html div class: 'container'; with: [...]) - Component Tree: Hierarchical component composition where components can contain other components, enabling complex UIs through simple building blocks
- Logical Encapsulation: Each component maintains its own DOM scope without relying on Shadow DOM, offering better CSS inheritance and easier debugging
- PharoJS Transpilation: Automatic and transparent conversion of Pharo methods to optimized JavaScript for browser execution
- Event System: Declarative event binding with automatic callback management and state synchronization
- Unified Language: Write frontend and backend logic in Pharo without JavaScript
- Productive Development: Leverage Smalltalk's interactive development environment and powerful IDE features
- Maintainability: Object-oriented architecture makes large applications easier to understand and maintain
- True Encapsulation: Components achieve full encapsulation without Shadow DOM, providing better CSS control and simpler debugging
- Testability: Comprehensive testing facilities built into Pharo and the framework (WIP)
- Composability: Build complex UIs from simple, reusable components
Your application entry point should subclass PjFileBasedWebApp:
MyWebApp class >> generateHtmlUsing: html
html div
id: 'app';
with: [ html paragraph with: 'my super app' ]The class-side generateHtmlUsing: method generates the initial HTML structure using the canvas tag brush API. This HTML can then be dynamically manipulated through the start method.
Components are the building blocks of WildCamping applications. Each component should subclass WCComponent and define:
MyComponent >> renderHtmlOn: html
"Define your component's HTML structure"
html div
id: 'someId';
with: 'Your content here'
MyComponent >> start
"Initialize event handlers and dynamic behavior"
(self getElementById: 'someId')
addEventListener: 'click'
do: [ self handleClick ]When a component is statically instantiated in Pharo and exported to JavaScript, it follows this lifecycle:
- Pharo Initialization (
initialize): Your component's instance is created with all instance variables initialized - HTML Generation (
renderHtmlOn:): The component's HTML structure is rendered into HTML file - JavaScript Export: The component instance is serialized in the application's JavaScript
- Browser Reconsolidation: When the page loads in the browser, the component instance is automatically reconsolidated and reconnected to its generated HTML
- Client Initialization (
start): Event handlers and dynamic behavior are initialized for this component instance
To integrate components into your application, your application class must use the StaticComponentHolder trait:
MyWebApp class >> traits
^ { StaticComponentHolder }The StaticComponentHolder trait provides the infrastructure to:
- Export statically instantiated components as HTML and JavaScript
- Automatically serialize component state and logic
- Reconsolidate components in the browser when the page loads
- Maintain proper component lifecycle management
This approach allows you to define component instances at the Pharo level and have them seamlessly available in the browser with their state preserved.
Each component maintains its own logical DOM scope through a lightweight scoping mechanism—without using Shadow DOM. When you call getElementById: within a component, it only returns elements created by that specific component. Elements with the same ID in nested subcomponents are not returned.
Why this is a plus:
- CSS Transparency: Your styles apply naturally throughout the component hierarchy without the complications of Shadow DOM style boundaries
- Easier Debugging: DOM remains fully visible and inspectable in browser DevTools without the abstraction overhead
- Simpler Integration: Components play nicely with existing CSS frameworks and global styles