Sugar
Super Global Artifacts
Sugar is a communication system in LemonadeJS that enables state sharing between components using a pub/sub pattern.
Components subscribe to named events with set
and trigger updates using dispatch
, allowing state synchronization across
the application. Components automatically update when changes are dispatched to their subscribed events.
Examples
Scope
Public Scope
In this example, the Profile component is globally registered with the alias My:Profile
, making it accessible throughout the application. The Loader component retrieves the registered Profile instance and updates its properties directly.
<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Profile() {
// Register the self under My:Profile alias
lemonade.set('My:Profile', this);
// Counter is created from the attribute counter
return render => render`<form>
<label>Name:</label><br/>
<input type="text" :bind="self.name" /><br/>
<label>Email:</label><br/>
<input type="text" :bind="self.email" />
</form>`;
}
function Loader() {
const load = function() {
// Get My:Profile self
let s = lemonade.get('My:Profile');
// Updates directly to the self properties
s.name = 'John Lennon';
s.email = 'john.lennon@beatles.com';
}
return render => render`
<input type="button" value="Load the data" onclick="${load}" />
`;
}
lemonade.render(Profile, document.getElementById('root'));
lemonade.render(Loader, document.getElementById('root'));
</script>
</html>
export default function Profile() {
// Register the self under My:Profile alias
lemonade.set('My:Profile', this);
// Counter is created from the attribute counter
return render => render`<form>
<label>Name:</label><br/>
<input type="text" :bind="self.name" /><br/>
<label>Email:</label><br/>
<input type="text" :bind="self.email" />
</form>`;
}
export default function Loader() {
const load = function() {
// Get My:Profile self
let s = lemonade.get('My:Profile');
// Updates directly to the self properties
s.name = 'John Lennon';
s.email = 'john.lennon@beatles.com';
}
return render => render`
<input type="button" value="Load the data" onclick="${load}" />
`;
}
Private Scope
Exposing the entire component object globally can raise security concerns. Instead, securely register specific actions to update properties or execute functions using the dispatch method.
<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
let { set, dispatch } = lemonade;
function Profile() {
set('my:profile', (s) => {
this.name = s.name;
});
// Counter is created from the attribute counter
return render => render`<form>
<label>Name:</label><br/>
<input type="text" :bind="${this.name}" /><br/>
</form>`;
}
function Loader() {
const update = function() {
// Send new values to another component using the dispatcher
dispatch('my:profile', {
name: 'John Lennon'
});
}
return render => render`<input type="button" value="Load" onclick="${update}" />`;
}
lemonade.render(Profile, document.getElementById('root'));
lemonade.render(Loader, document.getElementById('root'));
</script>
</html>
import { set, dispatch, onchange } from 'lemonadejs';
export default function Profile() {
set('profile:updateName', (s) => {
this.name = s.name;
});
// Counter is created from the attribute counter
return render => render`<form>
<label>Name:</label><br/>
<input type="text" :bind="${this.name}" /><br/>
</form>`;
}
export default function Loader() {
const update = function() {
// Send new values to another component using the dispatcher
dispatch('profile:updateName', {
name: 'John Lennon'
});
}
return render => render`
<input type="button" value="Load" onclick="${update}" />
`;
}
Persistence
To illustrate this concept, we'll use an example similar to the one above, utilizing the persistence flag to maintain the last dispatched data saved, even after a page refresh.
<html>
<script src="https://lemonadejs.com/v4/lemonade.js"></script>
<div id='root'></div>
<script>
function Profile() {
// Create a blank self for this component
const self = this;
// Register the dispatcher under Profile
// and set the persistence as true
lemonade.set('persistence:test', function(s) {
self.name = s.name;
self.email = s.email;
}, true);
// The template create the form elements
return `<form>
<label>Name:</label><br/>
<input type="text" :bind="self.name" /><br/>
<label>Email:</label><br/>
<input type="text" :bind="self.email" />
</form>`;
}
function Loader() {
const self = this;
self.dispatch = function() {
// Send new values to another component using the dispatcher
lemonade.dispatch('persistence:test', {
name: 'John Lennon',
email: 'john.lennon@beatles.com',
});
}
return `<input type="button" value="Load" onclick="self.dispatch()" />`;
}
lemonade.render(Profile, document.getElementById('root'));
lemonade.render(Loader, document.getElementById('root'));
</script>
</html>
Summary of This Chapter
This chapter explores key aspects of Sugar:
-
Global Access: Leverage Sugar's set and get methods to make
self
properties globally accessible across components. - Action Registration: Register actions to make them available across different components.
- Data Dispatchers: Execute cross-component actions while preserving their private scope and ensuring security and encapsulation.
- Persistent State: Utilize the persistence flag to retain the latest data state.
What's Next?
Learn more about unit testing with LemonadeJS.
Unit testing