How to use Showdown in Angular and NodeJS

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ⭐🙏
When managing my dev bookmarks via www.codever.dev, I often have the need to place in the bookmark’s description either a code snippet (might be a command), add a list or add links and emphasize words - this seems like a perfect match for Markdown1. In this post I will demonstrate how I enabled Markdown support with the help of ShodownJS2, both in front end (developed with Angular3) and in backend, developed with NodeJS4.
Source code for Codever.dev is available on Github -
Star
What is Showdown?
So what it Showdown? Well, according to the its authors “showdown is a Javascript Markdown to HTML converter, based on the original works by John Gruber. Showdown can be used client side (in the browser) or server side (with NodeJs).”1
How to use Showdown in Angular?
I use showdown in front end everytime I create a new bookmark or I update one. The notes written in Markdown are transformed in html and persisted in the database. I thought it would be more performant than just to persist the markdown code and generate html on the fly.
Showdown dependencies
First thing is to add the proper dependencies @types/showdown
and showdown
in package.json
:
"dependencies": {
"@angular/common": "2.4.2",
"@angular/compiler": "2.4.2",
"@angular/core": "2.4.2",
"@angular/forms": "2.4.2",
"@angular/http": "2.4.2",
"@angular/platform-browser": "2.4.2",
"@angular/platform-browser-dynamic": "2.4.2",
"@angular/router": "3.4.2",
"@types/showdown": "^1.4.31",
"bootstrap": "^4.0.0-alpha.5",
"core-js": "^2.4.1",
"immutable": "^3.7.6",
"keycloak-js": "2.3.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.1",
"showdown": "^1.6.4",
"zone.js": "^0.7.2"
},
Then and npm install
and you’re ready to go.
Code setup
I am building the application with Webpack 2.x5, so I need to import showdown/dist/showdown.js
in the vendor.ts file:
// Angular 2
import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';
import 'rxjs';
import '@angularclass/hmr';
// Other vendors for example jQuery, Lodash or Bootstrap
// You can import js, ts, css, sass, ...
import 'bootstrap/dist/css/bootstrap.css';
import "keycloak-js/dist/keycloak.js";
import "showdown/dist/showdown.js";
Now the library is ready to be used in code. First require the showdown
module, then create a showdown converter
import {Component, OnInit} from "@angular/core";
import {Bookmark} from "../../model/bookmark";
import {FormGroup, FormBuilder, Validators} from "@angular/forms";
import {KeycloakService} from "../../keycloak/keycloak.service";
import {UserBookmarkStore} from "../../personal/store/UserBookmarkStore";
import {Router} from "@angular/router";
import {BookmarkService} from "../../bookmark/bookmark.service";
const showdown = require('showdown');
const converter = new showdown.Converter();
and finally call the makeHthml
method on the converter
, before the bookmark is created and persisted:
saveBookmark(model: Bookmark) {
model.tags = model.tagsLine.split(",");
var newBookmark = new Bookmark(model.name, model.location, model.category,model.tagsLine.split(","), model.description, null);
newBookmark.userId = this.userId;
newBookmark.shared = model.shared;
newBookmark.descriptionHtml = converter.makeHtml(newBookmark.description);
let obs = this.userBookmarkStore.addBookmark(this.userId, newBookmark);
obs.subscribe(
res => {
console.log(res);
this.router.navigate(['/personal']);
});
}
I use the same approach when I update a bookmark. Just generate new html from the description and persist it.
How to use Showdown in NodeJS?
At this point, there isn’t much of a need to use showdown in the back-end, but I kept it for two reasons:
- there might be cases when the descriptionHtml not present is, like API calls
- I get to write about how to do it in this post, maybe I’ll still need it later
Similar to front-end, add the showdown
dependency in package.json
and then npm install
it:
{
"name": "bookmarks-api.codepedia.org",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.15.1",
"cheerio": "latest",
"cookie-parser": "~1.4.3",
"debug": "~2.2.0",
"express": "~4.13.4",
"jade": "~1.11.0",
"keycloak-connect": "^2.5.0",
"mongoose": "~4.3.7",
"mongoose-unique-validator": "1.0.2",
"morgan": "~1.7.0",
"request": "latest",
"serve-favicon": "~2.3.0",
"showdown": "^1.6.4"
},
"devDependencies": {
"nodemon": "^1.10.2"
}
}
Then require the showdown
module and create a showdown converter
:
var showdown = require('showdown'),
converter = new showdown.Converter();
and finally use the makeHtml
method of the converter
in the post
and put
routes:
router.post('/:id/bookmarks', keycloak.protect(), function(req, res, next){
var descriptionHtml = req.body.descriptionHtml ? req.body.descriptionHtml: converter.makeHtml(req.body.description);
.............
});
...............
router.put('/:userId/bookmarks/:bookmarkId', keycloak.protect(), function(req, res, next) {
if(!req.body.descriptionHtml){
req.body.descriptionHtml = converter.makeHtml(req.body.description);
}
Bookmark.findOneAndUpdate({_id: req.params.bookmarkId, userId: req.params.userId}, req.body, {new: true}, function(err, bookmark){
.............
});
});
Works like a charm.
If you liked this, please show some love and give us a star Star