Creating a Live Auction App with Angular 2, Node.js and Socket.IO

In this tutorial we’re going to build a very cool Live Auction Application, my goal is to demonstrate how Angular 2, Node.js and Socket.io work together, and as everyone knows Angular 2 will soon come out so I thought it would be great to write a tutorial involving it. Of course our app won’t have all the features a real auction app has, this is just an example to demonstrate how to integrate the frameworks I mentioned.
Setting up the Environment
The first step is to configure the environment, let’s get started by creating our Node.js project, I’m going to use express to do it, if you don’t have it open your terminal and run the command npm install express-generator -g
to install, then you can create the project with the command expess {project name}
(replace {project name}
with your actual project name).
Now you have the basic project structure created for you, but we need to make some changes in order add Angular 2 and Socket.io to the project, first open your package.json
and paste the following code:
{ "name": "auction", "version": "0.0.0", "private": true, "scripts": { "postinstall": "npm run typings install", "tsc": "tsc", "tsc:w": "tsc -w", "start": "concurrent \"node ./bin/www\" \"npm run tsc:w\"", "typings" : "typings" }, "license": "ISC", "dependencies": { "body-parser": "~1.13.2", "cookie-parser": "~1.3.5", "debug": "~2.2.0", "express": "~4.13.1", "jade": "~1.11.0", "morgan": "~1.6.1", "serve-favicon": "~2.3.0", "angular2": "2.0.0-beta.6", "systemjs": "0.19.20", "es6-promise": "^3.0.2", "es6-shim": "^0.33.3", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.0", "zone.js": "0.5.14", "socket.io":"1.4.5" }, "devDependencies": { "concurrently": "^1.0.0", "lite-server": "^2.0.1", "typescript": "^1.7.5", "typings":"^0.6.8" } } |
Now create another file called tsconfig.json
in the project root folder and put the following into the file:
{ "compilerOptions": { "target": "es5", "module": "system", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false }, "exclude": [ "node_modules", "typings/main", "typings/main.d.ts" ] } |
Also in the root folder create the typings.json
and copy/paste the following code into it:
{ "ambientDependencies": { "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim /es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2" } } |
Ok, we have now created all the configuration files, but before we continue you have to install the dependencies by running the command npm install
on the terminal.
If you open your app.js
you’ll see that express has already put some code in there, in order to make it as simple as possible let’s delete the code we don’t need, after the modification your app.js
should look like this:
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var routes = require('./routes/index'); var app = express(); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); module.exports = app; |
By default express sets jade as the view engine, we’ve already deleted the lines responsible for that in the app.js
, but if you check the folder /views
you’ll see some .jade
files, you can delete them if you want, later we’re going to place our Angular 2 HTML pages in this folder, but for now you can just create and leave an empty index.html
in there.
After getting rid of the .jade
files we have to tell Node.js that we want to use our index.html
instead, we can do that by changing a little bit the code in the routes/index.js
file, just open the file and replace its content with the following code:
var express = require('express'); var router = express.Router(); var path = require('path'); /* GET home page. */ router.get('/', function(req, res, next) { res.sendFile(path.join(__dirname, '../', 'views', 'index.html')); }); module.exports = router; |
To make sure it’s working run your project with the command npm start
then enter the url http://localhost:3000/
in your browser, it should display an empty page, write something in the index.html
you created and refresh the page, if it appears on the browser you’ve done everything right! Now we’re ready to actually start writing some code.
Introducing Socket.IO
As we know, an auction app must be real-time, which means that when a user places a bid all the other connected users must instantly see it, that’s where Socket.IO comes in, it allows Node.js to receive and broadcast events to all connected clients, so when a user places a bid Socket.IO will emit an event to the server which will update the product price and broadcast it to the other users.
Now let’s see how that looks like in code, copy the following code and paste bellow the var app = express();
in your app.js
:
var http = require('http'); var server = http.createServer(app); var io = require('socket.io').listen(server); server.listen(8000); io.set("origins", "*:*"); var currentPrice = 99; io.on('connection', function (socket) { socket.emit('priceUpdate',currentPrice); socket.on('bid', function (data) { currentPrice = parseInt(data); socket.emit('priceUpdate',currentPrice); socket.broadcast.emit('priceUpdate',currentPrice); }); }); |
In the first 5 lines I’m just creating the server that Socket.IO will use to send/receive events, then I’m creating a listener to the connection
event which will be emitted every time a user connects (we’ll get to that in a moment), you can use the function io.on(event, callback)
to create a listener to any event you want, you just need to pass the event as the fist argument and the callback function as the second, when it receives the event the callback will be executed.
In this case when a user connects I’m immediately emitting the event priceUpdate
passing the current price, that’s because at this moment the client-side doesn’t have the current price yet, so that’s the first thing we want to send. After that we have to create a listener to the bid
event that will update the price and broadcast to the other users. Note that I’m calling socket.emit()
and socket.broadcast.emit()
, the first one will return the updated price to the user that triggered the event, and the second will do the same to all the other users.
Writing our First Angular 2 Component
Now that we have our server-side ready we can get started with Angular 2, let’s create a folder called app
inside public/javascripts/
on our project, that’s where we’re going to put all our Angular 2 code. Now create a file and name it app.component.ts
, then paste the following code into it:
import {Component} from 'angular2/core'; @Component({ selector: 'auction-app', templateUrl: 'templates/product.html' }) export class AppComponent { price: number = 0.0; socket = null; bidValue = ''; constructor(){ this.socket = io('http://localhost:8000'); this.socket.on('priceUpdate', function(data){ this.price = data; }.bind(this)); } bid(){ this.socket.emit('bid', this.bidValue); this.bidValue = ''; } } |
When declaring a @Component
, you have to pass an object containing two attributes selector
and templateUrl
(or just template
), the selector
is the name we’re going to use to call our component, in this case it’s auction-app
, and the templateUrl
is the HTML implementation, you can put your HTML code directly on the object or you can put it in a separated file like I did.
We also have in this code the AppComponent
class, that’s kind of the ‘controller’ of our component, in the constructor I’m connecting to the Socket.IO from the server using the method io('http://localhost:8000')
, this will emit the connection
event we saw before, then I’m creating a listener to the priceUpdate
event, the server will emit it right after the connection, remember? When Angular receives the updated price it will assign it to the price
variable. This class also contains the bid()
function, it will just emit the bid
event passing the bidValue
.
I forgot to mention that I created the /template
folder inside /views
, the problem is that our component doesn’t have access to it, to solve this problem we have to make this folder a static route on our server, open your app.js
and add this line:
app.use('/templates', express.static(__dirname + '/views/templates/')); |
Now let’s take a look on our template, I’ll not show the full HTML code here, just the lines that interact with our AppComponent
class.
<h1>$ {{price}}</h1> <input [(ngModel)]="bidValue" > <button (click)="bid()">Place Bid</button> |
There is not much to explain here, the syntax resembles a lot the first Angular, our input
uses the attribute [(ngModel)]
to reference the bidValue
variable from our class, and the button uses the (click)
to call the function bid()
.
To be able to launch our application we have to create a file called main.ts
on the public/javascripts/
folder, just copy/paste the following into the file:
import {bootstrap} from 'angular2/platform/browser' import {AppComponent} from './app.component.js' bootstrap(AppComponent); |
Finally we can write our index.html
, you have already created it in the beginning, remember? It’s located on the /views
folder, open it and add the following code:
<html> <head> <title>Live Auction</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script> <script src="scripts/es6-shim/es6-shim.min.js"></script> <script src="scripts/systemjs/dist/system-polyfills.js"></script> <script src="scripts/angular2/bundles/angular2-polyfills.js"></script> <script src="scripts/systemjs/dist/system.src.js"></script> <script src="scripts/rxjs/bundles/Rx.js"></script> <script src="scripts/angular2/bundles/angular2.dev.js"></script> <script src="javascripts/jquery.js"></script> <script src="javascripts/bootstrap.min.js"></script> <link href="stylesheets/bootstrap.min.css" rel="stylesheet"> <link href="stylesheets/portfolio-item.css" rel="stylesheet"> <script> System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } } }); System.import('javascripts/app/main.js') .then(null, console.error.bind(console)); </script> </head> <body> <auction-app>Loading...</auction-app> </body> </html> |
Once again we have to edit the app.js
file to be able to load the scripts directly from the node_modules
folder, add the following line like we did before:
app.use('/scripts', express.static(__dirname + '/node_modules/')); |
This file is pretty simple, we have a script that configures System.js to load our application, and in the body we can call our component by the selector we specified before: auction-app
.
Now we’re done!! You can run your application by opening your terminal and running the command npm start
, the result should similar to the gif in the beginning of this tutorial.
Just leave a comment if you have any problems, I’ll be glad to help!!
In your
System.config (index.html)
I believe you can replaceTo
This way you don’t have to write the “js” extension everytime you import a component.
Ex:
instead of:
or
instead of:
Thanks for the tip man!
Hey man thank you for the tutorial. I have ran through about 10 of these tonight (No Joke) but always the same outcome… I have some error that keeps me from the end goal.
This is seems to be the most simple and easiest to build on so I would really like to get this one working. I went through your tutorial about 5 times and I still have these same errors.
When I run npm start the loaded page just says loading as in the html file. In the console I get,
and in the term
Any ideas?
has anyone got any solutions to this problem yet? I have ran across the same issue here.
Thanks
I think my problem here is that the typescript in all of the applications I have made tonight are not being compiled to js.
But why are your compiling when you run npm start and mine are not after following your project verbatim?
What does your terminal show right after you run npm start?
I changed this,
I now I get this
Hard to tell what’s wrong, I’ve just uploaded the project to github: https://github.com/leonardohjines/auctionApp, try this version and see if it makes any difference.
Bro, Thank you!
Weird… It works now.
I think maybe some of the stuff in the project is missing from the tut. Like when do you add bootstrap to the javascripts folder?
You’re right, I’m sorry, I forgot to mention it on the tutorial, but that is not what caused the compilation problem, the project works fine even without bootstrap and jQuery.
Oh I agree. I was just mentioning it because when troubleshooting it is quite confusing when there are broken links in the program on top of whatever other problem I was having. Thanks for all your help. This is actually the cleanest startup example of mean angular2 I could find. I think it would be really awesome if you could clarify the things you forgot as well as an explanation of typescript. I suspect typescript was my problem and it will probably cause a lot of people headaches in the near future.
Bro, you’ve many things missing that makes it annoying for starters. Make that clear and point to your github repo or add the missing files and details as of product.html.
Thanks for the tuto
Is the source of your project available for download?
Yes, here’s the link: https://github.com/leonardohjines/auctionApp
Please update to angular 2 release candidate
Please update to angular 2 release candidate
Great job. Thanks a lot for publishing
Thank you for the tutorial. Great work
Great Tutorial!!! I will build on it. Thanks brother!!!
Great, one little thing, you shouldn’t mixe. Component should never do a service job.
I built an app and i’m planning to make a real time battle with Angular 2 and laravel. For example, you hit the “attack” button, and your opponent see his life going down in real time.
Thank you for the tutorial.
But I wonderful how to deploy that source on my public host, not localhost?
It is a very useful post/article/demo, Leonardo Jines!
For those using the GitHub version, who may get ‘peer dependencies’ or ‘cannot export promise’ errors, please use this package.json (please note that the name of the project is changed):