How Do I Broadcast Json To Users via Node JS server? - node.js

Goal: After user saves data to my mysql DB, a JSON teaser of the newly saved content is broadcasted to all users.
What are some quick and dirty solutions to achieve this? I am working with php/mysql. Got a taste of Socket.io, and a node.js server listening on the side....
Any good links would be greatly appreciated.
Background--------
After many years of working with php, I have decided to jump onto the NodeJS Bandwagon- and have no clue to do anything other than the vanilla "Hello World".... This is my starting point. Thanks again!!

If you would like to broadcast to all users including yourself, then you should use:
io.sockets.emit('global', {data: 'this will be received by everyone'});
If you would like to broadcast to all users without yourself being notified, then you should use:
socket.broadcast.emit('Hello everyone!');
Hope this helps!

Can you define what you mean by "broadcast" in this context?
Do you want to display an alert on a web page?
If so, do you wish to alert only those users who are currently viewing pages on your site or those who come to your site during a period of time? If the later, do you care if the alert appears to a single user more than once?
Socket.IO is one way to transmit/receive messages to/from a remote browser, but without a client-side handler that has some way of displaying your message to the user, it's kind of moot.
I've found now.js to be a far better abstraction of browser<-->server communications as shown in this possible solution that will display an alert on pages currently viewed by your users:
Step 1: Install now.js (remove the -g if you don't want now installed globally):
npm install -g now
Step 2: Create a HTTP server and cause it to listen on a port 80
var PORT=80,
http=require('http'),
nowjs=require('now'),
app=http.createServer(requestHandler).listen(PORT), // create your server
everyone=nowjs.initialize(app); // initialize now on your listening server
function alertUsers(msg){ // sends msg to showAlert() callback on clients
everyone.now.showAlert(msg);
}
function requestHandler(req,res) {
...
...
/*
* Ok, something has happened you want to inform your currently
* connected users of...
*
* if the thing can happen as a result of a request, do it here
*/
var msg="Whoohoo! Something's happened!";
alertUsers(msg);
...
...
}
// or if it happens somewhere else, just call alertUsers() there.
Step 3: And then in the browser-side HTML:
<html>
<head>
<style type="text/css">
.alert { background-color:red; color:white; font:24pt bold sans-serif; }
.hidden { display:hidden; visibility:none; }
</style>
</head>
<body>
<div id="alert" class="alert hidden">
<div class="main-content">
<!-- main page content -->
</div>
<script type="text/javascript" src="...load jQuery here..."></script>
<script src="/nowjs/now.js"></script> <!-- *SEE NOTE BELOW -->
<script type="text/javascript>
$(document).ready(function(){
// after the document has finished loading
now.showAlert=function(msg){ // define your callback function
$('#alert').removeClass('hidden').text(msg);
};
});
</script>
</body>
</html>
* Note that the <script src="/nowjs/now.js"></script> request is magic in that the referenced file doesn't really exist server-side at that location. By running nowjs.initialize() on your http server instance, you're setting things up so that nowjs will intercept the request for /nowjs/now.js and serve it back to the client without invoking your requestHandler().
Also, this solution does not display your alert to anyone who connects after the alert is sent from the server. If you want to do that, you'll need to do something different.

Related

How do I get a Electron app object through HTML (script tag)?

So I have here this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Oh no!</title>
</head>
<body>
<label>Oh dear. A serious error occurred and the app needs to restart. Press the button below to restart.</label>
<br>
<button onclick="restart()">Restart</button>
<script>
const { app } = require("electron")
function restart() {
app.relaunch()
app.exit()
}
</script>
</body>
</html>
And now, when the app receives an unhandled error this will show... but when the user clicks the button, the app doesn't restart so how would I make the app restart?
You can't get the app object without using preload.js and neither is directly getting the app object safe. There is a method to do the above using preload.js and ipcRenderer which are pure Electon APIs
In electron (even in web development), there is server-side code and browser-side code. The code written in between the script tags in your snippet is server side code which will fail to execute in browser side.
Server-side code in your case is in NodeJS Backend and browser-side code is the one which is the HTML Page and its own javascript.
So to close the window (which only NodeJS can do, i.e., the backend) you need to use Electron's ipcRenderer which helps string based communication between the browser-side javascript and the server-side javascript.
While creating a browser window in electron using new BrowserWindow(options) where options is an object. Define the object as:
options = {
webPreferences: {
preload: preload.js, //You need to create a file named preload.js (or any name) in your code
nodeIntegration: true,
contextIsolation: false,
}
}
Now in a new file called preload.js:
window.ipcRenderer = require('electron').ipcRenderer;
In your snippet you added const { app } ... which should be done this way to inject the javascript using a preload property in the object.
Now in the main app.js file (whatever you named maybe index.js) where you created the browser window:
const ipc = require('electron').ipcMain; //Add to your pre-existing code
ipc.on("close-app", (event, message) => { //"close-app" can be anything but, you need to use the same key in the send message side (later in this answer)
browserWindow.close(); //If you named the browserwindow as browserWindow
});
Now in your HTML (i.e., send message side)
...
<script>
window.ipcRenderer("close-app", ""); //Second parameter is used if you want to send some extra message. The extra message can be viewed in the server side from the message parameter in the app.js code (just above this paragraph)
</script>
This is a bit difficult if you are doing it for the first time.
I've added more articles which will help you clear your confusions:
Highlight about server-side and browser-side code
Relation with socket.io communication in NodeJS

Hide other code in the app.js in the login page of a vue app

I'm creating an app using laravel + vue. Vue loads all the javascript codes in the app.js even in the login page in which the user is not yet authenticated. I don't want to expose all the js codes just yet until user is logged in. Is there any way that I can only show the necessary codes for the login page and not expose all the js code of the app? I'm doing this to also limit the size of app.js being downloaded for the login page to improve page loading time too.
If you're using Laravel mix, you can add a new entry point for your login script.
mix.js('resources/assets/app/js/app.js', 'public/app/js')
.js('resources/assets/app/js/login.js, 'public/app/js);
So, you can add a #yield statement in your base layout to place scripts declared in the page view.
base.blade.php
<html>
<head>
<title>App Name - #yield('title')</title>
</head>
<body>
#section('sidebar')
This is the master sidebar.
#show
<div class="container">
#yield('content')
</div>
#yield('scripts')
</body>
</html>
login.blade.php
#extends('layouts.base')
#section('title', 'Page Title')
#section('content')
<p>This is my body content.</p>
#stop
#section('scripts')
<script src="{{ mix('app/js/login.js') }}"></script>
#stop
Edited
For a SPA you can have another bundled entry point to just define the Login component:
Vue.component('login-form', {
... component code here
})
You can use vue-plugin-load-script to load the login.js script in the beforeEach method from Vue router. So the component will be available before render the login page.
router.beforeEach(async (to, from, next) => {
if (to === 'login'){
await Vue.loadScript('login entry point url here')
}
next();
});
Also, you can do it inside the page using the beforeMount hook. Here an example using a fake promise and entry point.
https://jsfiddle.net/7oj2sxun/3/
If you're using Vue CLI, it already supports lazy loading with dynamic imports.
Here a detailed article:
https://blog.logrocket.com/lazy-loading-in-vue-js/

Running a node.js file from a click event

I am new to node.js. I am connecting with an api with express/node.js with ejs templating. I wanted to push some information from the browser to the api. At the moment, I can push from the command line. I understand I cannot call a node.js file from the browser directly but I was wondering when I click submit on a form if it can call node.js file and run it...what should I use, modules, routes, ajax, or any other solutions you recommend? I appreciate the help.
Well, it's a strange question. Your Node application needs to either listen for HTTP requests or WebSocket connections (possibly using some abstraction like Socket.io that can provide fallbacks where WebSocket is not available) and handle the requests or messages sent on the socket. In those handlers you can do whatever you need. This is just a basic principle of client/server architecture.
Now, to choose what technology is best suited for your needs it all depends on how often you need to make that communication, whether or not you need realtime communication, how much data do you need to pass around, if you need to support serving data initiated by the server or only by the client etc.
To answer you question - yes, you can submit a form and make it execute some code in your Node application. This is exactly how forms work - they make a connection, usually GET with data in the query string or POST with data in the body (both can easily be read by Node) and then your handler on the backend handles the request, does whatever it needs and sends a response.
Consider this simple example using express and body-parser:
const app = require('express')();
const { urlencoded } = require('body-parser');
app.use(urlencoded({ extended: true }));
app.use('/', (req, res) => {
const { method, path } = req;
const { x } = req.body;
console.log(`Client request: ${method} ${path} (x = ${x})`);
res.end(`
<!doctype html>
<html>
<head>
<title>Form handling example</title>
</head>
<body>
<p>x = ${x}</p>
<form method="POST" action="/">
Enter x: <input type="text" name="x">
<input type="submit" value="Submit">
</form>
</body>
</html>
`);
});
app.listen(4447, () => console.log('Listening on http://localhost:4447/'));
Create a new directory, save this code as server.js, run:
npm init -y
npm install express body-parser -S
node server.js
and access the printed URL in the browser.
When you click the submit button you'll see what will happen in the Node app.
Your node app will have a route set up which accepts GET or POST requests. Then it does some logic and returns some data in a response.
Your web page will send the form to your node route as GET or POST via an AJAX call, and likewise it will receive a response from the AJAX call. Then you can take this response and use it locally in the webpage however you like.

Why would I need template engines like Jade or EJS on the backend?

I am familiar with Angularjs(1.x) and use templates in directives.
Currently I am learning nodejs and as a part of the course template engines are mentioned. What are the advantages of using them on the backend?
Currently I can't see any use.
If you have data (say from a database) that needs to be rendered to HTML, you can use a template engine to take the data and a template and render it to HTML (which subsequently gets served to the client).
If your frontend app does the same, using XHR calls or something similar to retrieve the data from the server, it's generally not useful to render to HTML server side (instead, the data gets sent as JSON to the client).
So it depends on how your app (both frontend and backend) is structured if it makes sense or not to use a templating engine.
There's also hybrid solutions where the initial HTML is rendered server side, and then the client side "takes over". This is something that, for instance, React supports. The big idea there is that you can use the same components both on the server and on the client, and that when a page is opened, the user will get to see a fully rendered initial page (instead of the client side having to retrieve the data from the backend first and then rendering the page).
You actually dont need them, but they have a lot of features that makes your pages more dynamic..
For example you can render just HTML using this code
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/index.html'));
//__dirname : It will resolve to your project folder.
});
But with engines you can send data to template.
http://expressjs.com/en/api.html#res.render
// pass a variable to the view
res.render('somePage', {
title: 'Awesome title',
userFriends: friendsList,
name: 'loggedUserName'
});
And now on front-end templates(EJS in this case) will populate html with data that you send in. So html became dynamic and you can make each page looks different for each user.
<ul>
<% for(var i=0; i<userFriends.length; i++) {%>
<li><%= userFriends[i] %></li>
<% } %>
</ul>
With just HTML you will need to make a lot of unnecessary AJAX calls to get and add data into html which is bad idea.
Hope this helps.
A view engine allows you to render HTML with options. For example, using squirrelly, I can create a file that looks like this:
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
{(userIsSignedIn){<!-- if the user is signed in, display username and description-->
<p>{{username}} is {{description}}</p>
}}
{(!userIsSignedIn){<!--if user isn't signed in, ask them to sign in-->
<p>Sign in to view this page</p>
}}
</body>
</html>
So I could listen, for example, to a dynamic user profile URL with Express and then return dynamic content.
It's almost 2020, Template literals are literally meant to replace template engines.
https://medium.com/#PaulBrowne83/do-we-really-need-template-engines-anymore-214eb6bc112e

Determine if an app exists and launch that app on iOS

Is there a way to check iOS to see if another app has been installed and then launched? If memory serves me this was not possible in early versions but has this been changed?
Doable, but tricky.
Launching installed apps, like the FB or Twitter apps, is done using the Custom URL Scheme. These can be used both in other apps as well as on web sites.
Here's an article about how to do this with your own app.
Seeing if the URL is there, though, can be tricky. A good example of an app that detects installed apps is Boxcar. The thing here is that Boxcar has advanced knowledge of the custom URL's. I'm fairly (99%) certain that there is a canOpenURL:, so knowing the custom scheme of the app you want to target ahead of time makes this simple to implement.
Here's a partial list of some of the more popular URL's you can check against.
There is a way to find out the custom app URL : https://www.amerhukic.com/finding-the-custom-url-scheme-of-an-ios-app
But if you want to scan for apps and deduce their URL's, it can't be done on a non-JB device.
Here's a blog post talking about how the folks at Bump handled the problem.
There is a script like the following.
<script type="text/javascript">
function startMyApp()
{
document.location = 'yourAppScheme://';
setTimeout( function()
{
if( confirm( 'You do not seem to have Your App installed, do you want to go download it now?'))
{
document.location = 'http://itunes.apple.com/us/app/yourAppId';
}
}, 300);
}
</script>
Calling this script from the web (Try to start MyApp), you can determine if your app with scheme "yourAppScheme" is installed on the device or not.
The App will launch if it is installed on the device and "yourAppScheme" is registered in it.
If the app is not installed you can suggest the user to install this app from iTunes.
To check if an app is installed (e.g. Clear):
BOOL installed = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"clearapp://"]];
To open that app:
BOOL success = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"clearapp://"]];
Hides the error message if the app is not installed
At Branch we use a form of the code below--note that the iframe works on more browsers. Simply substitute in your app's URI and your App Store link.
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
window.onload = function() {
// Deep link to your app goes here
document.getElementById("l").src = "my_app://";
setTimeout(function() {
// Link to the App Store should go here -- only fires if deep link fails
window.location = "https://itunes.apple.com/us/app/my.app/id123456789?ls=1&mt=8";
}, 500);
};
</script>
<iframe id="l" width="1" height="1" style="visibility:hidden"></iframe>
</body>
</html>
There's a second possibility that relies on cookies first and the javascript redirect only as a fallback. Here's the logic:
When a user without the app first taps on a link to your app, he or she is redirected straight to the App Store. This is accomplished by a link to your app actually being a dynamically-generated page on your servers with the redirect. You create a cookie and log a "digital fingerprint" of IP address, OS, OS version, etc. on your backend.
When the user installs the app and opens it, you collect and send another "digital fingerprint" to your backend. Now your backend knows the link is installed On any subsequent visits to links associated with your app, your servers make sure that the dynamically-generated redirect page leads to the app, not the App Store, based on the cookie sent up with the request.
This avoids the ugly redirect but involves a ton more work.
To my understanding, because of privacy issues, you can't see if an app is installed on the device. The way around this is to try and launch the app and if it doesn't launch to have the user hit the fall back url. To prevent the mobile safari error from occurring I found that placing it in an iframe helps resolve the issue.
Here's a snippet of code that I used.
<form name="mobileForm" action="mobile_landing.php" method="post">
<input type="hidden" name="url" value="<?=$web_client_url?>">
<input type="hidden" name="mobile_app" value="<?=$mobile_app?>">
<input type="hidden" name="device_os" value="<?=$device_os?>">
</form>
<script type="text/javascript">
var device_os = '<? echo $device_os; ?>';
if (device_os == 'ios'){
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100)
return;
document.forms[0].submit(); }, 5);
var redirect = function (location) {
var iframe = document.createElement('iframe');
iframe.setAttribute('src', location);
iframe.setAttribute('width', '1px');
iframe.setAttribute('height', '1px');
iframe.setAttribute('position', 'absolute');
iframe.setAttribute('top', '0');
iframe.setAttribute('left', '0');
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
setTimeout(function(){
window.close()
}, 150 );
redirect("AppScheme");
I struggled with this recently, and here is the solution I came up with. Notice that there is still no surefire way to detect whether the app launched or not.
I serve a page from my server which redirects to an iPhone-specific variant upon detecting the User-Agent. Links to that page can only be shared via email / SMS or Facebook.
The page renders a minimal version of the referenced document, but then automatically tries to open the app as soon as it loads, using a hidden <iframe> (AJAX always fails in this situation -- you can't use jQuery or XMLHttpRequest for this).
If the URL scheme is registered, the app will open and the user will be able to do everything they need. Either way, the page displays a message like this at the bottom: "Did the app launch? If not, you probably haven't installed it yet .... " with a link to the store.

Resources