Today we’ll create a number of controls that allow you to add notifications (think of Growl) to your website.
To do that, we need to create two controls: A Notification
and a NotificationArea
. The Notification
will be the actual notification that displays your message to the user, while
the NotificationArea
will be an area on the screen where the notifications should be displayed.
A Notification
will consist of a few properties:
- type
- The type of the notification. We’ll support
info
,warning
,error
andsuccess
by default. - title
- The title of the notification.
- content
- The content that should be displayed in the notification.
- icon
- An icon that should be displayed next to the content.
- closeable
- A boolean property that specifies whether or not the notification can be closed by the user.
- showTime
- An integer that describes how long the notification should be visible before it disappears automatically. Setting it to
0
means that the user has to close it manually.
A NotificationArea
is a bit simpler. It only needs an ObservableList
of notifications and an edge
property that describes where it should be displayed. edge
allows five values:
- inline
- The default setting will keep the notification area where it is.
- top-left
- Displays the area at the top left corner of the page.
- top-right
- Displays the area at the top right corner of the page.
- bottom-right
- Displays the area at the bottom right corner of the page.
- bottom-left
- Displays the area at the bottom left corner of the page.
We’ll also want a Notifier
class that acts as a singleton to add notifications to a global NotificationArea
which, by default, will be placed at the top right corner. And, in order to
use nice default icons, we’ll also use a NotificationIcon
object that loads some SVG icons from a file.
The NotificationArea
Let’s start with the NotificationArea
.
[cc lang=”javascript”]define([
‘jidejs-extra/Class’,
‘jidejs/base/ObservableList’,
‘jidejs/ui/Control’,
‘jidejs/ui/Skin’,
‘text!./NotificationArea.html’
], function(Class, ObservableList, Control, Skin, NotificationAreaTemplate) {
// Define the class
var NotificationArea = Class({
$extends: Control,
$init: function(config) {
config || (config = {});
if(!config.children) this.children = new ObservableList();
else this.children = ObservableList(config.children);
delete config.children;
Control.call(this, config);
},
edge: Class.Property({ value: ‘inline’ }),
// This method should be used to add notifications to the area
add: function(notification) {
this.children.insertAt(0, notification);
}
});
NotificationArea.Skin = Skin.create(Skin, {
template: NotificationAreaTemplate,
install: function() {
Skin.prototype.install.call(this);
var area = this.component;
area.classList.add(‘jide-extra-notification-area’);
// Note: We need to dynamically add/remove class names, there is no binding for that
// kind of task yet so we need to do it here
this.managed(area.edgeProperty.subscribe(function(event) {
area.classList.remove(‘edge-‘+event.oldValue);
area.classList.add(‘edge-‘+event.value);
}));
area.classList.add(‘edge-‘+area.edge);
// registers an event listener for the ‘notification:closed’ event which is fired by
// Notification controls that are children of this control.
// This takes advantage of the event bubbling in jide.js controls.
this.on({
‘notification:closed’: this.handleNotificationClosed.bind(this)
});
},
handleNotificationClosed: function(event) {
// remove the notification from the list
var notification = event.source,
area = this.component,
index = area.children.indexOf(notification);
if(index > -1) {
area.children.removeAt(index);
event.stopImmediatePropagation();
event.stopPropagation();
}
}
});
// Add an enum for the supported edge locations.
NotificationArea.Edge = {
INLINE: ‘inline’,
TOP_LEFT: ‘top-left’,
BOTTOM_LEFT: ‘bottom-left’,
BOTTOM_RIGHT: ‘bottom-right’,
TOP_RIGHT: ‘top-right’
};
return NotificationArea;
});[/cc]
One feature that we’re using here is the ability of jide.js controls to fire bubbling DOM events, i.e. they traverse the DOM tree until their propagation is stopped. Thus, we just listen for the notification:closed
event that is fired by notifications whenever they are closed.
The Notification Control
[cc lang=”javascript”]define([
‘jidejs-extra/Class’,
‘jidejs/base/Observable’,
‘jidejs/ui/Control’,
‘jidejs/ui/Skin’,
‘text!./Notification.html’
], function(
Class, Observable,
Control, Skin,
NotificationTemplate
) {
var Property = Class.Property;
var Notification = Class({
$extends: Control,
$init: function(config) {
Control.call(this, config || {});
},
title: Property({ value: ” }),
content: Property({ value: ” }),
type: Property({ value: ‘info’ }),
icon: Property,
closeable: Property({ value: true }),
showTime: 0
});
Notification.Skin = Skin.create(Skin, {
template: NotificationTemplate,
install: function() {
var notification = this.component;
this.isVisible = Observable(true);
Skin.prototype.install.call(this);
notification.classList.add(‘jide-extra-notification’);
// again, we can’t add or remove dynamically named classes (sorry!) so we just need to listen for it here
this.managed(notification.typeProperty.subscribe(function(event) {
notification.classList.remove(event.oldValue);
notification.classList.add(event.value);
}));
notification.classList.add(notification.type);
// start a timeout timer if the Notification should only be visible for a specific time
if(notification.showTime) {
this.timeoutId = setTimeout(this.closeNotification.bind(this), notification.showTime);
}
},
closeNotification: function() {
// this is the event that the NotificationArea is listening for!
this.component.emit(‘notification:closed’);
this.isVisible.set(false);
clearTimeout(this.timeoutId);
}
});
return Notification;
});[/cc]
Okay, as you can see this one is pretty simple, too. Whenever the control is closed, we fire the notification:closed
event.
Add some templates
At this point, we’ve already created both controls, all we need is some templates so that jide.js knows how to render these controls.
Let’s start with the NotificationArea again:
[cc lang=”html”]
[/cc]
Okay, that’s kind of boring, isn’t it? We’re just using a foreach
binding here to list all notifications for this area. Now, let’s look at the Notification
template:
[cc lang=”html”]
[/cc]
The inline SVG contains our close icon, that’s why we’ve added an event listener to it. Everything else about this template is probably old news for you.
At this point, I’d like to spare you the CSS and the SVG for the icons and finish with the Notifier
object:
[cc lang=”javascript”]define([
‘jidejs/base/Util’,
‘jidejs-extra/control/NotificationArea’,
‘jidejs-extra/control/Notification’,
‘jidejs-extra/control/NotificationIcon’
], function(_, NotificationArea, Notificiation, NotificationIcon) {
var exports = {},
notificationArea = null;
function initNotificationArea() {
if(notificationArea === null) {
notificationArea = new NotificationArea({
edge: NotificationArea.Edge.TOP_RIGHT
});
document.body.appendChild(notificationArea.element);
}
}
var add = exports.add = function(notification) {
initNotificationArea();
notificationArea.add(notification);
};
var addInfo = exports.addInfo = function(title, content, config) {
config || (config = {});
_.defaults(config, { type: ‘info’, title: title, content: content, showTime: 5000, closeable: true, icon: NotificationIcon.info });
add(new Notificiation(config));
};
var addWarning = exports.addWarning = function(title, content, config) {
config || (config = {});
_.defaults(config, { type: ‘warning’, title: title, content: content, showTime: 5000, closeable: true, icon: NotificationIcon.warning });
add(new Notificiation(config));
};
var addSuccess = exports.addSuccess = function(title, content, config) {
config || (config = {});
_.defaults(config, { type: ‘success’, title: title, content: content, showTime: 5000, closeable: true, icon: NotificationIcon.success });
add(new Notificiation(config));
};
var addError = exports.addError = function(title, content, config) {
config || (config = {});
_.defaults(config, { type: ‘error’, title: title, content: content, showTime: 5000, closeable: true, icon: NotificationIcon.error });
add(new Notificiation(config));
};
Object.defineProperty(exports, ‘edge’, {
get: function() {
initNotificationArea();
return notificationArea.edge;
},
set: function(edge) {
initNotificationArea();
notificationArea.edge = edge;
}
});
return exports;
});[/cc]
As you can see, it contains a bunch of useful methods to help you create new notifications.
Putting it all together
Now let’s take a look at how to use the Notifier
:
[cc lang=”javascript”]require([
‘jidejs/ui/layout/VBox’,
‘jidejs/ui/control/Button’,
‘jidejs-extra/control/Notifier’
], function(VBox, Button, Notifier) {
Notifier.addInfo(‘Welcome’, ‘This demo shows how to create a new note notification.’, { showTime: 0 });
document.body.appendChild(new VBox({
children: [
new Button({
text: ‘Notify Info’,
onaction: function() {
Notifier.addInfo(‘Info notification’, ‘
This notification informs you of something.
I hope you enjoy it.
‘);
}
}),
new Button({
text: ‘Notify Warning’,
onaction: function() {
Notifier.addWarning(
‘Warning’,
‘You’re still looking at a demo!
Go download jide.js right now!’
);
}
}),
new Button({
text: ‘Notify Error’,
onaction: function() {
Notifier.addError(‘Error notification’, ‘There has been an error, don’t panic!’);
}
}),
new Button({
text: ‘Notify Success’,
onaction: function() {
Notifier.addSuccess(‘Success’, ‘Good job!’);
}
})
]
}).element);
});[/cc]
We’re using a VBox
control to display a set of icons that can be used to create add notifications. Note that we’re using the onaction
property in the button configuration parameter
instead of the usual [cci_javascript]on: { action: function() { /* … */ } }[/cci] to save a few characters.
As always, the control is available in the jidejs-extra repository.