express-flash-notification
Advanced tools
Comparing version 0.2.0 to 0.3.0
255
index.js
@@ -0,1 +1,6 @@ | ||
/** | ||
* Provides Flash Notification Middleware | ||
* | ||
* @module Express Flash Notification | ||
*/ | ||
var format = require('util').format | ||
@@ -5,18 +10,91 @@ var isArray = require('util').isArray | ||
var REDIRECT = true | ||
var SESSION_NAME = 'flash' | ||
var UTILITY_NAME = 'flash' | ||
var LOCALS_NAME = 'flash' | ||
var VIEW_NAME = 'flash' | ||
var BEFORE_SINGLE_RENDER = function(item, callback){callback(null, item)} | ||
var AFTER_ALL_RENDER = function(htmlFragments, callback){callback(null, htmlFragments.join('\n'))} | ||
/** | ||
* Default value for when calling the `req.flash` method withouth specifying | ||
* the redirect argument. | ||
* | ||
* @property REDIRECT | ||
* @type Boolean|String | ||
* @final | ||
*/ | ||
var REDIRECT = true | ||
/** | ||
* Default name of property stored in req.session that holds an array of notifications | ||
* to display. | ||
* | ||
* @property SESSION_NAME | ||
* @type String | ||
* @final | ||
*/ | ||
var SESSION_NAME = 'flash' | ||
/** | ||
* Default name of method stored in `req` object used to trigger a request notification. | ||
* | ||
* @property UTILITY_NAME | ||
* @type String | ||
* @final | ||
*/ | ||
var UTILITY_NAME = 'flash' | ||
/** | ||
* Default locals property name used to render the flash notification markup. | ||
* | ||
* @property LOCALS_NAME | ||
* @type String | ||
* @final | ||
*/ | ||
var LOCALS_NAME = 'flash' | ||
/** | ||
* Default view template filename (extension) that will be passed to tthe express | ||
* template engine to generate the flash notification markup. | ||
* | ||
* @property VIEW_NAME | ||
* @type String | ||
* @final | ||
*/ | ||
var VIEW_NAME = 'flash' | ||
/** | ||
* Default callback that is called before each notification is rendered using the | ||
* express template engine. | ||
* | ||
* @method BEFORE_SINGLE_RENDER | ||
* @final | ||
*/ | ||
var BEFORE_SINGLE_RENDER = function(item, callback){callback(null, item)} | ||
/** | ||
* Default callback that is called after all notifications have been rendered by the | ||
* express template engine. | ||
* | ||
* @method BEFORE_SINGLE_RENDER | ||
* @param htmlFragments {Array} | ||
* @final | ||
*/ | ||
var AFTER_ALL_RENDER = function(htmlFragments, callback){callback(null, htmlFragments.join('\n'))} | ||
/** | ||
* Utility used to check whether an argument is a Native Object | ||
* | ||
* @method isObject | ||
* @return Boolean | ||
* @private | ||
*/ | ||
function isObject(sample) | ||
{ | ||
return (typeof sample === 'object' && !isArray(sample)) | ||
return (sample && typeof sample === 'object' && !isArray(sample)) | ||
} | ||
function ConnectMiddleware (app, options) | ||
/** | ||
* Function used to expose express instance and configuration options. | ||
* The actual middleware is returned. | ||
* | ||
* @method Module | ||
* @param app {Express} | ||
* @param options {Object} | ||
*/ | ||
function Module (app, options) | ||
{ | ||
// Are we configuring the middleware? | ||
if (isObject(options)) | ||
@@ -28,18 +106,84 @@ { | ||
VIEW_NAME = options.view_name || VIEW_NAME | ||
BEFORE_SINGLE_RENDER = options.beforeSingleRender || BEFORE_SINGLE_RENDER | ||
AFTER_ALL_RENDER = options.afterAllRender || AFTER_ALL_RENDER | ||
BEFORE_SINGLE_RENDER = (typeof options.beforeSingleRender === 'function') | ||
? options.beforeSingleRender | ||
: BEFORE_SINGLE_RENDER | ||
AFTER_ALL_RENDER = (typeof options.afterAllRender === 'function') | ||
? options.afterAllRender | ||
: AFTER_ALL_RENDER | ||
} | ||
function Middleware(req, res, next) | ||
/** | ||
* Render Notifications on queue | ||
* | ||
* @method render | ||
* @private | ||
*/ | ||
function render (req, res, next) | ||
{ | ||
if (!req.session) throw new Error('express-session is required') | ||
if (!isArray(req.session[SESSION_NAME])) req.session[SESSION_NAME] = [] | ||
if (req.session[SESSION_NAME].length === 0) | ||
{ | ||
next() | ||
} | ||
else | ||
{ | ||
var resultHTML = [] | ||
async.each( | ||
req.session[SESSION_NAME], | ||
function(item, callback) | ||
{ | ||
BEFORE_SINGLE_RENDER(item, function(err, item){ | ||
if (err) return callback(err) | ||
app.render(VIEW_NAME, item, function(err, html) { | ||
if (err) return callback(err) | ||
resultHTML.push(html) | ||
callback(null) | ||
}) | ||
}) | ||
}, | ||
function(err) | ||
{ | ||
if (err) return next(err) | ||
req.session[SESSION_NAME].length = 0 | ||
AFTER_ALL_RENDER(resultHTML, function(err, html){ | ||
if (err) return next(err) | ||
res.locals[LOCALS_NAME] = html | ||
next() | ||
}) | ||
} | ||
) | ||
} | ||
} | ||
// Flash utility | ||
/** | ||
* Adds flash method to req object and renders all notifications found in | ||
* req.session | ||
* | ||
* @method FlashMiddleware | ||
*/ | ||
function FlashMiddleware(req, res, next) | ||
{ | ||
if (!isObject(req.session)) | ||
{ | ||
throw new Error('express-session is required') | ||
} | ||
else | ||
{ | ||
if (!isArray(req.session[SESSION_NAME])) | ||
{ | ||
req.session[SESSION_NAME] = [] | ||
} | ||
} | ||
/** | ||
* Utility used to programmatically add flash notifications | ||
* | ||
* @method Flash Utility | ||
*/ | ||
req[UTILITY_NAME] = function() | ||
{ | ||
var notification | ||
var redirect = REDIRECT | ||
var argc = arguments.length | ||
var redirect = REDIRECT | ||
var argc = arguments.length | ||
// Parse arguments | ||
if (argc === 1) | ||
@@ -50,10 +194,7 @@ { | ||
{ | ||
notification = arg | ||
redirect = (arg.redirect === undefined) ? redirect : arg.redirect | ||
notification = arg, redirect = (arg.redirect === undefined) ? redirect : arg.redirect | ||
} | ||
else | ||
{ | ||
notification = { | ||
message: arg + '', | ||
} | ||
notification = { message: arg + ''} | ||
} | ||
@@ -63,6 +204,3 @@ } | ||
{ | ||
notification = { | ||
type: arguments[0] + '', | ||
message: arguments[1] + '', | ||
} | ||
notification = { type: arguments[0] + '', message: arguments[1] + '' } | ||
redirect = (arguments[2] === undefined) ? redirect : arguments[2] | ||
@@ -75,4 +213,10 @@ } | ||
if (notification) req.session[SESSION_NAME].push(notification) | ||
// Queue Notification | ||
if (notification) | ||
{ | ||
req.session[SESSION_NAME].push(notification) | ||
} | ||
// If redirect is set, refresh or redirect, accordingly. Otherwise, render the | ||
// notifications now since it's on this request where they will be displayed. | ||
if (redirect) | ||
@@ -83,42 +227,25 @@ { | ||
} | ||
} | ||
// Check if there are any messages in session to render | ||
if (req.session[SESSION_NAME].length) | ||
{ | ||
var resultHTML = [] | ||
async.each( | ||
req.session[SESSION_NAME], | ||
function(item, callback) | ||
else | ||
{ | ||
/** | ||
* When there is no redirect, notifications must be rendered now and since | ||
* rendering is async (and this method is sync), a *promise* like function is returned. | ||
* The function can be called with a callback that will be called after all notifcations | ||
* are rendered, otherwise, rendering will be done during the next request. | ||
*/ | ||
return function ManualRender(callback) | ||
{ | ||
BEFORE_SINGLE_RENDER(item, function(err, item){ | ||
if (err) return callback(err) | ||
app.render(VIEW_NAME, item, function(err, html) { | ||
if (err) return callback(err) | ||
resultHTML.push(html) | ||
callback(null) | ||
}) | ||
}) | ||
}, | ||
function(err) | ||
{ | ||
if (err) throw err | ||
req.session[SESSION_NAME].length = 0 | ||
AFTER_ALL_RENDER(resultHTML, function(err, html){ | ||
if (err) throw err | ||
res.locals[LOCALS_NAME] = html | ||
next() | ||
}) | ||
render(req, res, callback) | ||
} | ||
) | ||
} | ||
} | ||
else | ||
{ | ||
next() | ||
} | ||
} | ||
return Middleware | ||
/** | ||
* Process Queued Notifications | ||
*/ | ||
render(req, res, next) | ||
} | ||
return FlashMiddleware | ||
} | ||
module.exports = ConnectMiddleware | ||
module.exports = Module |
{ | ||
"name": "express-flash-notification", | ||
"version": "0.2.0", | ||
"description": "Express.js flash notifications that work with any template engine", | ||
"version": "0.3.0", | ||
"description": "Express.js flash notifications that works with any template engine", | ||
"main": "index.js", | ||
@@ -6,0 +6,0 @@ "scripts": { |
220
README.md
# express-flash-notification | ||
This module provides a way to set one-time notifications to be displayed after processing a request. Notifications are stored in session and are removed once they have been displayed. | ||
This module provides a way to set one-time notifications to be displayed during or after processing a request. Notifications are stored in session and are removed once they have been rendered. | ||
**Key Points** | ||
* Template Engine Agnostic, works with any engine you are using logic/logicless | ||
* Supports for Multiple notifications to be sent | ||
* Auto refreshes the page to display the notification | ||
* Allows you to manipulate the notification output before and after it has been created | ||
- **Template Engine Agnostic**, works with any engine you are using logic/logicless. | ||
- Supports for **Multiple notifications** to be sent. | ||
- **Auto refreshes or redirects** the page to display the notification. | ||
- Allows you to manipulate the notification output **before and after** it has been created. | ||
- **No need to refresh or redirect**, notifications can be rendered on the same request. | ||
@@ -18,18 +19,21 @@ ### Why? | ||
`req.flash('info', 'my message')` in my controller/middleware and | ||
`{{{flash}}}` in my layout | ||
`req.flash('info', 'my message')` | ||
in my controller/middleware and | ||
`{{{flash}}}` | ||
in my layout | ||
Then the HTML for each alert gets placed in `{{{flash}}}` including some client side javascript so it doesn't just appear `$(elm).slideDown()` **FTW** | ||
While at the same time, adding the markup nessary to display the alert depending on its **type** (so info is blue, error is red, etc) | ||
While at the same time, adding the markup nessary to display the alert depending on its **type** (so *info* is blue, *error* is red, etc). | ||
## Install | ||
$ npm install express-flash-notification --save | ||
``` | ||
npm install express-flash-notification --save | ||
``` | ||
## Usage | ||
Flash notifications are stored in the session. You will need the `cookieParser` middleware and the `session` middleware. Depending on your express version it may be bundled in express or for the newer releases you'll have to npm install them as seperate modules. I'm using express 4.x | ||
Flash notifications are stored in the session. You will need the `cookieParser` middleware and the `session` middleware. Depending on your express version it may be bundled in express or for the newer releases you'll have to npm install them as seperate modules. I'm using express 4.x in the following examples. | ||
You must pass the express application instance as the first argument in the `flash()` middleware so `app.render` can be used to create your notification using your template engine and views directory | ||
You must pass the express application instance as the first argument in the `flash()` middleware so `app.render` can be used to create your notification using your chosen template engine and views directory. | ||
@@ -50,4 +54,81 @@ ```javascript | ||
With the `flash` middleware in place, all requests will have a `req.flash()` function that can be used for flash notifications. | ||
##### In your Layout | ||
Wherever you place the local variable `flash`, it will be populated with the notifications if there are any. Make sure it does not escape, as the output *may* be HTML. | ||
```html | ||
<<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title></title> | ||
</head> | ||
<body> | ||
{{{flash}}} | ||
</body> | ||
</html> | ||
``` | ||
##### In your Views | ||
By default, a view called `flash` in your `views` directory will be retrieved and used as the default template for your notifications. | ||
The local variables `type` and `message` will be set, depending on the type and message passed when calling **req.render** | ||
**flash.html** (I'm using mustache in this example) | ||
```html | ||
<div class="alert flash"> | ||
<button type="button" class="close">×</button> | ||
<i class="fa sign"></i><strong>{{type}}</strong> | ||
<span>{{message}}</span> | ||
</div> | ||
``` | ||
### req.flash API | ||
**Note** A `notification` is an object where its properties become the local variables when rendering the it using the express rendering engine of your choice. | ||
- **req.flash**(*String*) | ||
Sets local variable `message` to the string provided, `type` will become an empty string. Will refresh the current page. | ||
- **req.flash**(*String*, *String*) | ||
First string is the `type` local variable, the second is the `message` local variable. Will refresh the current page. | ||
- **req.flash**(*String*, *String*, *String*) | ||
Similar to to above, except last argument as a string defines which page to redirect to. | ||
- **req.flash**(*String*, *String*, *Boolean*) | ||
Same as above. Third variable as a boolean decides whether or not to refresh the page. | ||
**NOTE** If set to false, notification will not be rendered until the next request. If you want the notification to be rendered on the current request, you can use a function that is returned by `req.flash`, simply call the function with a callback, the callback will be executed once rendering is complete. | ||
**example** | ||
``` | ||
app.all(function SampleExpressRoute(req, res){ | ||
var manualRender = req.flash('warn', 'tell them now!', false) | ||
manualRender(function(err){ | ||
if (err) throw err | ||
res.render('layouts/internal') | ||
}) | ||
}) | ||
``` | ||
- **req.flash**(*object*) | ||
You can pass an object as the first argument, the object's properties will be exposed as local variables when rendering the notification template. | ||
The property `redirect` is reserved and functions just as you'd expect; a *Boolean* determines if it will refresh, or as a *String* you specify where to redirect to. | ||
```javascript | ||
req.flash('info', 'if cats ruled the world', false) | ||
``` | ||
is treated exactly the same as: | ||
```javascript | ||
req.flash({ | ||
type: 'info', | ||
message: 'if cats rules the world', | ||
redirect: false | ||
}) | ||
``` | ||
### Usage Example | ||
With the `flash` middleware in place, all requests will have a `req.flash()` method to send out flash notifications. | ||
```javascript | ||
@@ -99,3 +180,6 @@ app.use('/login', function loginProcessor(req, res, next){ | ||
By default, req.flash will redirect to the current url, effectively refreshing the page so to display the flash notification. It's important that your logic uses `return` when using flash or contraints so you don't get the *headers have already been sent* error. | ||
###### Pitfalls | ||
By default, **req.flash** will redirect to the current url, effectively refreshing the page so to display the flash notification. It is important that your logic uses `return` when using flash or conditioning so you don't get the *headers have already been sent* error. | ||
For example below, if 2 + 2 ever equals 'fish' the `req.flash` method will send out the redirect headers, and execution will continue until the `next` function is called, `next` will also try to set the response headers | ||
@@ -116,3 +200,2 @@ | ||
In the case above, and in case you want to send multiple notifications you can disable the redirect by setting the third parameter to `false` | ||
You can also set a string and that will become the destination for the redirect | ||
@@ -132,49 +215,2 @@ ```javascript | ||
##### Using req.flash | ||
A notification is basically an object where its properties become the local variables when rendering the notification | ||
- req.flash(*String*) | ||
Sets local variable `message` to the string provided, `type` will become an empty string. Will refresh page | ||
- req.flash(*String*, *String*) | ||
First string is the `type` local variable, the second is the `message` local variable. Will refresh page. | ||
- req.flash(*String*, *String*, *Boolean*) | ||
Same as above. Third variable as a boolean decides whether or not to refresh the page. | ||
- req.flash(*String*, *String*, *String*) | ||
Similar to to above, except last argument as a string defines which page to redirect to | ||
- req.flash(*object*) | ||
You can pass an object as the first argument, the object's properties will be exposed as local variables when rendering the notification template. | ||
The property `redirect` is reserved and functions just as you'd expect; a Boolean determines if it will refresh, or as a String you specify where to redirect to. | ||
`req.flash('info', 'if cats ruled the world', false)` is treated exactly the same as | ||
`req.flash({ | ||
type: 'info', | ||
message: 'if cats rules the world', | ||
redirect: false | ||
})` | ||
##### In your Layout | ||
Wherever you place the local variable `flash`, it will be populated with the notifications if there are any. Make sure it does not escape, as the output will be HTML | ||
##### In your Views | ||
By default, a view called `flash` in your `views` directory will be retrieved and used as the default template for your notifications. | ||
The local variables `type` and `message` will be set. | ||
**flash.html** (I'm using mustache in this example) | ||
```html | ||
<div class="alert flash"> | ||
<button type="button" class="close">×</button> | ||
<i class="fa sign"></i><strong>{{type}}</strong> | ||
<span>{{message}}</span> | ||
</div> | ||
``` | ||
---------------- | ||
@@ -184,3 +220,3 @@ | ||
When setting the flash middleware, the second parameter accepts an object for configuration | ||
When setting the flash middleware, the second parameter accepts an object for configuration. | ||
Below is an example with all the options set to their defaults | ||
@@ -213,13 +249,13 @@ | ||
----------------- | ||
## Advance Usage | ||
Heres an example where custom notifications will be rendered, `beforeSingleRender` is used to add class names depending on the `type` of notification | ||
so the resulting notification looks different depending on its type. Also, `afterAllRender` will be used to append some javascript so notification | ||
don't just appear, they slide into view. | ||
Heres an example where custom notifications will be rendered, `beforeSingleRender` is used to add class names depending on the `type` of notification so the resulting notification looks different depending on its type. Also, `afterAllRender` will be used to append some javascript so notification don't just appear, they slide into view. | ||
**NOTE** `{{{flash}}}` is placed in my layout template, not shown here | ||
**NOTE** `{{{flash}}}` is placed in my layout, not shown here | ||
This is my `flash.html` view template. | ||
`alert_class` and `icon_class` will be populated inside of `beforeSingleRender` | ||
`style="display: none"` is set so the appended javascript uses jQuery's slideDown method to animate its presentation | ||
`style="display: none"` is set so the appended javascript uses jQuery's `slideDown` method to animate its presentation | ||
@@ -230,3 +266,3 @@ ```html | ||
<i class="fa {{icon_class}} sign"></i><strong>{{type}}</strong> | ||
<span>{{message}}</span> | ||
<span>{{{message}}}</span> | ||
</div> | ||
@@ -241,52 +277,52 @@ ``` | ||
view_name: 'elements/flash', | ||
beforeSingleRender: function(item, callback) | ||
beforeSingleRender: function(notification, callback) | ||
{ | ||
if (item.type) | ||
if (notification.type) | ||
{ | ||
switch(item.type) | ||
switch(notification.type) | ||
{ | ||
case 'error': | ||
item.alert_class = 'alert-danger' | ||
item.icon_class = 'fa-times-circle' | ||
notification.alert_class = 'alert-danger' | ||
notification.icon_class = 'fa-times-circle' | ||
break; | ||
case 'alert': | ||
item.alert_class = 'alert-warning' | ||
item.icon_class = 'fa-times-circle' | ||
notification.alert_class = 'alert-warning' | ||
notification.icon_class = 'fa-times-circle' | ||
break; | ||
case 'info': | ||
item.alert_class = 'alert-info' | ||
item.icon_class = 'fa-times-circle' | ||
notification.alert_class = 'alert-info' | ||
notification.icon_class = 'fa-times-circle' | ||
break; | ||
case 'success': | ||
item.alert_class = 'alert-success' | ||
item.icon_class = 'fa-check' | ||
notification.alert_class = 'alert-success' | ||
notification.icon_class = 'fa-check' | ||
break; | ||
case 'ok': | ||
item.alert_class = 'alert-primary' | ||
item.icon_class = 'fa-check' | ||
notification.alert_class = 'alert-primary' | ||
notification.icon_class = 'fa-check' | ||
break; | ||
} | ||
} | ||
callback(null, item) | ||
callback(null, notification) | ||
}, | ||
afterAllRender: function(htmlFragments, callback) | ||
{ | ||
// Naive JS is appened, waits a while expecting for the DOM to finish loading in 200ms, | ||
// The timeout can be removed if jOuery is loaded before this is called | ||
// Naive JS is appened, waits a while expecting for the DOM to finish loading, | ||
// The timeout can be removed if jOuery is loaded before this is called, or if you're using vanilla js. | ||
htmlFragments.push([ | ||
'<script type="text/javascript">', | ||
' setTimeout(function(){', | ||
' $(".alert.flash").slideDown().find(".close").on("click", function(){$(this).parent().hide()})', | ||
' var timer = setInterval(function(){', | ||
' if (window.jOuery){', | ||
' clearInterval(timer)', | ||
' $(".alert.flash").slideDown().find(".close").on("click", function(){$(this).parent().slideUp()})', | ||
' }', | ||
' }, 200)', | ||
'</script>', | ||
].join('')) | ||
callback(null, htmlFragments.join('')) | ||
}, | ||
})) | ||
``` | ||
And this is how I use it | ||
**And this is how you'd use it** | ||
@@ -293,0 +329,0 @@ ```javascript |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20291
227
335
1