I want to be able to flash a message to the client with Express and EJS. I've looked all over and I still can't find an example or tutorial. Could someone tell me the easiest way to flash a message?
Thanks!
I know this is an old question, but I recently ran across it when trying to understand flash messages and templates myself, so I hope this helps others in my situation. Considering the case of Express 4, the express-flash module, and an ejs template, here are 2 routes and a template that should get you started.
First generate the flash message you want to display. Here the app.all() method maps to /express-flash. Request baseurl/express-flash to create a message using the req.flash(type, message) setter method before being redirected to baseurl/.
app.all('/express-flash', req, res ) {
req.flash('success', 'This is a flash message using the express-flash module.');
res.redirect(301, '/');
}
Next map the message to the template in the res.render() method of the target route, baseurl/. Here the req.flash(type) getter method returns the message or messages matching the type, success, which are mapped to the template variable, expressFlash.
app.get('/', req, res ) {
res.render('index', {expressFlash: req.flash('success') });
}
Finally, display the value of expressFlash, if it exists, in index.ejs.
<p> Express-Flash Demo </p>
<% if ( expressFlash.length > 0 ) { %>
<p>Message: <%= expressFlash %> </p>
<% } %>
Then start the server and visit baseurl/express-flash. It should trigger a redirect to baseurl/ with the flash message. Now refresh baseurl/ and see the message disappear.
<% if ( message ) { %>
<div class="flash"><%= message %></div>
<% } %>
Is this what you want? You can use some client-side JS to have it fading out. jQuery example:
var message = $( '.message' );
if ( message.length ) {
setTimeout( function() {
message.fadeOut( 'slow' );
}, 5000 );
}
req.flash() can be used in two ways.
If you use two arguments
req.flash(type, message);
where type is a type of message and message is actual message (both strings), then it adds message to the queue of type type. Using it with one argument
req.flash(type);
returns array of all messages of type type and empties the queue. Additionally this method is attached to the session, so it works per session. In other words, each user has its own set of flash queues. So in your view you can do something like this:
var messages = req.flash('info');
and then send the messages variable to the template and use it there (note that messages is an array and you can iterate it). Remember that using req.flash('info', 'test'); will append a test message of type info only for a current user (associated with the req object).
Keep in mind that this mechanism is quite weak. For example if a user double clicks on link (two requests send to the server), then he will not see messages, because the first call will empty the queue (of course unless the request generates messages).
I was struggling with this as well; Getting my flash message to appear in my .ejs.
BUT, I finally got it to WORK and this is what I understand.
Once the getter req.flash('_msgName_'); is called it is cleared!
Also when you app.use(session()); cookie must equal cookie: {maxAge: 720}, essentially a big number.
I was using a console.log() to test the getter, and it was displaying in my console, but not in my .ejs. I took the console.log() out and it worked.
Here is some of my code.
Server.js ~
app.get('/', function(req, res) {
// This is where we will retrieve the users from the database and include them in the view page we will be rendering.
User.find({},function(err, allUsers){
if(err){
console.log("Oh no we got an error\n ERROR :: "+err);
} else {
// console.log(allUsers);
res.render('index',{users : allUsers, msg : req.flash('vError')});
}
});
});
// Add User Request
app.post('/users', function(req, res) {
console.log("REQUESTED DATA:\t", req.body);
var user = new User(
{
name: req.body.name,
age: req.body.age
}
);
// Saves user to DB.
user.save(function(err) {
if(err) {
console.log('OOPS, Something went Wrong... \n ERROR :: '+err+'\n');
for(var key in err.errors){
// console.log(err.errors[key].message);
req.flash('vError', err.errors[key].message);
}
// **HERE I WAS ACCIDENTALLY CLEARING IT!!!** \\
// console.log(req.flash('vError'));
// res.render('index',{users : [], msg : req.flash('vError')});
res.redirect('/');
} else {
console.log('\tSuccessfully added a new User to the DB!');
res.redirect('/');
}
})
});
index.ejs ~
<% if(msg.length > 0) { %>
<% for (var error in msg) { %>
<h3><%= msg[error] %></h3>
<% } %>
<% } %>
If you use visionmedia's express-messages helper module, it becomes very simple. Github link
As it says in the module's docs:
Install the module with npm
npm install express-messages
You then assign a dynamic helper for messages in the app.config:
app.dynamicHelpers({ messages: require('express-messages') });
In your EJS file, insert the following where you want your messages
<%- messages() %>
EJS renders this into:
<div id="messages">
<ul class="info">
<li>Email queued</li>
<li>Email sent</li>
</ul>
<ul class="error">
<li>Email delivery failed</li>
</ul>
</div>
(of course, you can alter what it renders to in the module's code)
Then, to actually flash a message, include a call to:
req.flash('info', 'Your message goes here');
Related
I'm currently stuck with a problem in a homework project. I'm trying to make a project where the price of bitcoin will update every second. Now the API request is working fine and I can see the data render from an EJS template but I can't make the price update every second. Can anyone check my code and see if anything is wrong in my code? For reference please check www.preev.com. It shows how I want the price to be updated. And also check below my code.
I have tried to call the API request in app.js file and rendered it in an EJS template called results.ejs.
app.js
var express = require("express");
var app = express();
var request = require("request");
app.set("view engine", "ejs");
app.get("/", function(req, res) {
request("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true", function(error, response, body) {
if(!error && response.statusCode == 200) {
var data = JSON.parse(body);
res.render("result", {data: data});
}
});
});
app.listen(3000, function(){
console.log("server has started");
});
results.ejs
<h1>
Bitcoin Latest
</h1>
Price: $ <span id="showPrice"></span>
<br>
MarketCap: $<%= data["bitcoin"]["usd_market_cap"] %>
<br>
24h Volume: $<%= data["bitcoin"]["usd_24h_vol"] %>
<br>
24h Change: <%= data["bitcoin"]["usd_24h_change"] %>%
<script>
function updatePrice(){
document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;
};
setInterval(updatePrice, 500);
</script>
Initial answer
Your setInterval works fine, it's just that inside your function the data never changes.
To fix it you have to reference a variable (of which the content changes), rather than hardcoding the value in your function.
Extra explanation
For example you are using EJS, which is a templating language. A templating language parses output based on your variables (once per page load).
Your template line
document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;
parses into
document.getElementById("showPrice").innerHTML= 9624.46;
And your interval then updates the innerHTML of #showPrice with that value, every 500 ms.
What you probably mean to do is make the request from the client (the browser), then store its response into a variable, say latestResult, and then code your function to reference that variable, like so:
document.getElementById("showPrice").innerHTML= latestResult;
Example implementation
This means that your express application (app.js) will render result without data:
app.get('/', function(req, res) {
res.render('result');
});
And the request part will be in your template:
function updateLatestPrice() {
fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true').then((result) => {
const latestResult = result.bitcoin.usd
document.getElementById("showPrice").innerHTML = latestResult || 'failed'
})
}
setInterval(updateLatestPrice, 3000)
Note that I changed request into fetch here because I couldn't be sure whether your client code has babel, so I went with the browser's native Fetch API.
In my main app.js file I have this piece of code:
app.use(function (req, res, next){
res.locals.currentUser = req.user;
next();
});
... which as you can see serves to get the user info from the passport. And it does it's job... that is until it is used more than once. Whenever I login as a user, and I hit any other routes, that should trigger the res.locals.currentUser to update... but it doesn't. It stays on the old value.. And I can't for the life of me figure out why. I've read the documentation and triple checked everything. Nothing seems to be out of order.
HERE is all of the code.
Most important files:
Main file
Header
Index Page
IMPORTANT NOTE: Every other instance of trying to get the user from passport by directly asking for it from passport works like a charm. Example is the line 58 in the main file:
res.render("campgrounds/index", {campgrounds: campgrounds, user1: req.user});
,that corresponds to lines 7-11 from the index page:
<% if(!user1) { %>
<h1> Welcome to YelpCamp! </h1>
<% } else { %>
<h1> <%= user1.username %>, Welcome to YelpCamp! </h1>
<% } %>
It returns the user data correctly every time. Any ideas why this is reacting this way? Thanks.
Got a link that removes a message from the current_users screen:
= link_to '[x]', msg, method: :delete, remote: true, class: "del-link"
It triggers this coffeescript-funktion:
delete_message = () ->
$('.del-link').on 'ajax:success', (event) ->
event.preventDefault()
$(this).closest('.message').remove()
return
and this rails-method:
def destroy
#msg = Msg.find(params[:id])
#msg.destroy
respond_to do |format|
format.html { redirect_to chat_path }
format.json { head :no_content }
format.js { render :layout => false }
end
end
But how would it be done, that the message is removed on every users screen, e.g. using ActionCable?
The coffeescript for the chat:
App.proom = App.cable.subscriptions.create "ProomChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
unless data.msg.blank?
$('#messages-table').append data.msg
scroll_bottom()
$(document).on "turbolinks:load", ->
submit_msg()
scroll_bottom()
delete_message()
the channel.rb
class ProomChannel < ApplicationCable::Channel
def subscribed
stream_from "proom_channel"
end
def unsubscribed
end
end
Approach
So I added this to the destroy-action of the controller
if msg.destroy
ActionCable.server.broadcast 'proom_channel', msg: #msg
end
Also I added an ID to every message-div-element containing the id of the message-record to find and remove it from the DOM with the following line
$("msg_#{msg.id}").remove()
But now I don't know where to put it.
I am not able to give you right now the complete answer, but I will update my answer later if you do not find a solution:
Like when you create a message with action cable, after saving the message in the DB, you trigger an ActionCable.server.broadcast 'messages' that will execute some JS included for example in the User Browser asset pipeline file app/assets/javascripts/channels/messages.js
This is my message controller, once I save the message, I start the Server.broadcast process. It will trigger the process in my messages_channel.rb that will update all clients that are subscribed to that channel.
def create
message = Message.new(message_params)
message.user = current_user 
chatroom = message.chatroom
if message.save
ActionCable.server.broadcast 'messages',
message: message.content,
user: message.user.name,
chatroom_id: message.chatroom_id,
lastuser: chatroom.messages.last(2)[0].user.name
head :ok
end
end
Image and text is from the following article of Sophie DeBenedetto
We add our new subscription to our consumer with
App.cable.subscriptions.create. We give this function an argument of
the name of the channel to which we want to subscribe,
MessagesChannel.
When this subscriptions.create function is invoked, it will invoke the
MessagesChannel#subscribed method, which is in fact a callback method.
MessagesChannel#subscribed streams from our messages broadcast,
sending along any new messages as JSON to the client-side subscription
function.
Then, the received function is invoked, with an argument of this new
message JSON. The received function in turn calls a helper function
that we have defined, renderMessage, which simply appends new messages
to the DOM, using the $("#messages") jQuery selector, which can be
found on the chatroom show page.
The channel will call the messages.js function received, that will append the div to the DOM, you will need to call an alternative action in messages.js.
App.messages = App.cable.subscriptions.create('MessagesChannel', {
received: function(data) {
// code executed - the parameters will be in data.chatroom_id and
// data.user, data.message etc...
}
});
So you will need to call the channel from your action messages#destroy, have in messages.js a specific function to remove the div from the DOM.
I don't know if you will need to create a specific server side channel or you can just edit your present messages_channel.rb to include a specific function.. You need to read the guides and figure this out.. I may try this in the future, but right now I can't
Or an easy alternative, just write some js in messages.js to solve this and have the div removed when required.. for example the messages#destroy action can pass a parameter and if that parameters is present, you will remove the message, instead of adding it
https://github.com/rails/rails/tree/master/actioncable#channel-example-1-user-appearances
I have a database on mlab.com that I can successfully post to using this code in my controller:
app.post('/dashboard', function(req, res) {
var newEvent = req.body;
db.collection('events').insertOne(newEvent, function(err, doc) {
if (err) {
error(res, "Failed to create new event.");
} else {
res.sendFile('views/dashboard.html', { root: '.' });
}
});
});
At the localhost:8080/dashboard URL, I have a form where a user can enter in event data.
Now my question is, how do I get that data to be able to display it in the HTML? For example, displaying a list of all the events entered at that same /dashboard URL but in a different location on the page. I believe I need to do something along these lines:
app.get('/dashboard', function (req, res) {
db.collection('events').findOne( {
eventName : req.body.eventName
}, function(err, doc) {
// ????
});
});
But then how do I display the data I got back in the HTML view?
Any help would be greatly appreciated because I've been stuck on this for quite some time now.
You have two options
1) Use a templating system like ejs or jade and render the page. For example if you are passing variables to a dashboard.ejs file located in views/pages folder, you call it like
res.render('views/pages/dashboard', {
feild1: value1,
feild2: value2
});
Then print it in dashboard.ejs using a code like
<div><%= feild1 %></div>
<div><%= feild2 %></div>
Lots of documentation is available in the net for both the options
2) Send data as json and use some javascript code or javascript based front end framework to render them. If you are good in front end javascript coding, you can go for this approach
var data = { "feild1" : doc.xxx, "feild2" : doc.yyyy };
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(data));
The json can be accessed using an ajax call from client side (you can use jquery for that)
$.getJSON('/dashboard', function(obj) {
alert(obj.feild1);
alert(obj.feild2);
});
I have a setup route in which the first user creates his account. If he already has an account he has to be redirected to the index route.
On the serverside i would do it like this:
if(Meteor.users.find().count > 0){
//do stuff
}
The users collection isn't published to the client. How can i return a simple true/false from the server to a route definition? I tried with Meteor.call('hasUser' function(result){
console.log(result);
});
But since call is async on the client i always get 'undefined' in my result
This has already been asked and answered here, however you could also do this with the publish-counts package:
$ meteor add tmeasday:publish-counts
server
Meteor.publish('userCount', function() {
Counts.publish(this, 'userCount', Meteor.users.find());
});
client
Meteor.subscribe('userCount');
...
Counts.get('userCount');
In addition, it also comes with helpers like:
<p>There are {{getPublishedCount 'userCount'}} users</p>