Basic Plugins CORE+
Learn how WildflowerJS plugins extend the framework using the same unified entity model as components, stores, and pool entities.
The Unified Entity Model
In WildflowerJS, plugins share the same reactive patterns as components and stores:
wildflower.component('x', {
state: { count: 0 },
computed: {
doubled() {
return this.count * 2
}
},
increment() {
this.count++
}
})
wildflower.store('x', {
state: { count: 0 },
computed: {
doubled() {
return this.count * 2
}
},
increment() {
this.count++
}
})
wildflower.plugin({
name: 'x',
state: { count: 0 },
computed: {
doubled() {
return this.count * 2
}
},
increment() {
this.count++
}
})
The differences are:
- Registration method:
wildflower.component()vsstore()vsplugin() - Access pattern: Plugins are accessed via
wildflower.$pluginName - Scope: Plugins are global and can extend framework behavior
- Capabilities: Plugins can register directives, hooks, and services
Plugin Registration
Basic Plugin
Plugins are registered using wildflower.plugin(). The simplest form is a function:
// Function-based plugin
wildflower.plugin(function(wf, options) {
// wf is the wildflower instance
// options are passed during registration
console.log('Plugin installed!')
})
// Object-based plugin with metadata
wildflower.plugin({
name: 'my-plugin',
version: '1.0.0',
install(wf, options) {
// Plugin installation logic
}
})
Plugin Options
Pass configuration options when registering a plugin:
wildflower.plugin(myPlugin, {
apiUrl: '/api',
debug: true
})
Plugin State
Creating Plugins with State
Plugins can have their own reactive state, just like components and stores:
wildflower.plugin({
name: 'notifications',
version: '1.0.0',
// Reactive state
state: {
items: [],
unreadCount: 0
},
// Methods at top level (same as components/stores)
add(message, type = 'info') {
this.items.push({ message, type, id: Date.now() })
this.unreadCount++
},
dismiss(id) {
const index = this.items.findIndex(n => n.id === id)
if (index !== -1) {
this.items.splice(index, 1)
}
},
clear() {
this.items = []
this.unreadCount = 0
},
// Computed properties
computed: {
hasUnread() {
return this.unreadCount > 0
}
},
install(wf) {
// Optional installation logic
}
})
Accessing Plugin State
Plugin state is accessible globally via $pluginName:
// Access state (shorthand: resolves through ContextProxy)
console.log(wildflower.$notifications.unreadCount)
// Call methods
wildflower.$notifications.add('New message!', 'success')
wildflower.$notifications.dismiss(123)
// Access computed (shorthand: no .computed. prefix needed)
if (wildflower.$notifications.hasUnread) {
// Show badge
}
// Reset state to initial values
wildflower.$notifications.reset()
// Explicit access still works too:
// wildflower.$notifications.state.unreadCount
// wildflower.$notifications.computed.hasUnread
Custom Directives
Registering Directives
Create custom HTML attributes with wildflower.directive():
// Register a tooltip directive
wildflower.directive('tooltip', {
init(element, value, context) {
// Called when element enters DOM
element.title = value
element.classList.add('has-tooltip')
},
update(element, value, oldValue, context) {
// Called when bound value changes
element.title = value
},
destroy(element, value, context) {
// Called when element leaves DOM
element.classList.remove('has-tooltip')
}
})
Using Directives
Use directives in HTML with data-{directive-name}:
<button data-tooltip="Click me!">Hover for tooltip</button>
<button data-tooltip="state.tooltipText">Dynamic tooltip</button>
Directive Context
The context parameter provides access to:
| Property | Description |
|---|---|
context.component |
The parent component instance |
context.path |
The bound state path |
context.resolvedValue |
The current resolved value |
context.listIndex |
Index when inside a list (or null) |
context.listItem |
List item data when inside a list |
Lifecycle Hooks
Available Hooks
Register callbacks for component lifecycle events:
// Hook into component initialization
wildflower.hook('component:beforeInit', (instance) => {
console.log('Component starting:', instance.name)
})
wildflower.hook('component:afterInit', (instance) => {
console.log('Component ready:', instance.name)
})
// Hook into component updates
wildflower.hook('component:beforeUpdate', (instance, changeInfo) => {
console.log('About to update:', changeInfo.path, changeInfo.newValue)
})
wildflower.hook('component:afterUpdate', (instance, changeInfo) => {
console.log('Updated:', changeInfo.path, 'from', changeInfo.oldValue, 'to', changeInfo.newValue)
})
// Hook into component destruction
wildflower.hook('component:beforeDestroy', (instance) => {
// Cleanup before component is destroyed
})
wildflower.hook('component:afterDestroy', (instance) => {
// After component is fully destroyed
})
Hook Use Cases
- Logging/Analytics: Track component lifecycle for debugging or analytics
- Performance Monitoring: Measure component initialization and update times
- State Synchronization: Sync state changes with external systems
- Cleanup: Ensure resources are properly released
Using Plugins in Components
Access plugins in computed properties for automatic dependency tracking:
wildflower.component('notification-bell', {
computed: {
// Plugin accessor is automatically reactive in computed properties!
unreadCount() {
const notifications = wildflower['$notifications']
return notifications ? notifications.unreadCount : 0
},
hasUnread() {
const notifications = wildflower['$notifications']
return notifications ? notifications.hasUnread : false
}
},
showNotification(message) {
// Access the global plugin in methods
wildflower.$notifications.add(message)
},
init() {
// Read plugin state in lifecycle
console.log('Unread:', wildflower.$notifications.unreadCount)
}
})
wildflower['$pluginName'] is accessed inside a computed property, the framework automatically:
- Detects which plugin properties are accessed
- Registers the component as a dependent of that plugin
- Re-evaluates the computed property when plugin state changes
$plugin.path, and complete plugin examples, see Advanced Plugin Patterns.