Loading...

Jammie Mountz

Software Engineer

Writing an SVG DOM element to Mongo Database

For a recent project, I wanted to use the D3 library to generate draggable elements for the user to arrange, and then save the image to the database. I found out there’s essentially two ways to ‘save’ an image for later retrieval: you can save the .png or .jpeg file to a directory and then have a path that serves that image, or you can ‘serialize’ the image and save it as a string into the database.

An SVG element is a DOM node that has information stored on it to render an image. Because of this, I decided to save it as a string and write it to my database. It was tricky for me, as a beginner to databases in general, to figure out how to successfully write and re-render this string. With some dedicated internet-research and purposeful copy/pasting, I arrived at the solution that I’ll detail here.

For this project, I’m running a MEAN stack: Angular as the front-end framework, supported by Express on Node.js as my server, using Mongo with Mongoose as my database. The model for my image data was called “wallpaper.” The mongoose schema looked like this:
[code lang=”js”]
var mongoose = require(‘mongoose’);
var WallpaperSchema = new mongoose.Schema({
serialized: String,
name: String,
date: { type: Date, default: Date.now },
love: { type: Number }
});
module.exports = mongoose.model(‘Wallpaper’, WallpaperSchema)
[/code]

Each wallpaper model had a name, a created date, and “love” which was just a way to store how many hearts a wallpaper recieved from users. The serialized field is what would store the string that had the data of the image of the wallpaper itself.

The D3 library allowed the user to select a background image and then place other images on top of it, dragging them around how they liked until they were ready to save. My DOM node would potentially look like the following:

[code lang=”html”]
<svg height="568" width="320" id="mySVG">
<image x="0" y="0" height="568" width="320" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.hdwallpapersinn.com/wp-content/uploads/2014/11/classic_aurora_background_iphone_wallpaper_download-640×1136.jpg"></image>
<image x="118" y="254" height="100" width="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://static.tumblr.com/2c0f3c037f4646e6592581371e21c7ca/vedxdqx/EL2n8vkwl/tumblr_static_ardaqo480qw40404okggk4osg.png"></image>
<image x="25" y="113" height="100" width="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.tinytowngoats.com/images/Does_-_Transparent_GIF_Goat_Bleu2.gif"></image><image x="194" y="52" height="100" width="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://i488.photobucket.com/albums/rr249/STACIA_GIRL_2009/ANIMALES/DOGS/CATS/FARM/GOAT.png"></image>
</svg>
[/code]

Notice that the entire element is a SVG with the id of “mySVG”, and then there’s information about what’s been appended on top and where it is. For science, this image ends up looking like this:

Screen Shot 2015-06-19 at 1.57.46 PM

With this image created, the user would click a “save” button and we’d move into Angular’s controller to handle the event.

Here’s what the angular controller looked like:
[code lang=”js”]
$scope.sendImage = function(){
var tmp = document.getElementById("mySVG");
var svg_xml = (new XMLSerializer).serializeToString(tmp);
Wallpaper.create({serialized: svg_xml, name: $scope.name});
$window.location.reload();
}
[/code]

Walking through this line by line:
We’re setting tmp to be the DOM element with the ID of “mySVG” – which, as referenced earlier, is the ID on the SVG tags.
We’re setting svg_xml to be the result of a new XMLSerializer object calling “serialize to string” on the tmp DOM element. This will stringify all the information in that element.
We’re then referencing out to an Angular factory method that I have already defined to pass that serialized information, along with some other information, as a POST request to the server.
Finally, we reload the window.

Looking in the database, I now have a string associated to each wallpaper that contains the SVG DOM node. Now, I needed to solve the problem in reverse: re-render that string as a node and append it to the DOM. I also wanted to use Angular’s ng-repeat to render the image for all the wallpaper data in the database.

To do this, I needed to create an Angular directive. This allows me to basically write my own HTML tag and give it “superpowers” – have it process what’s entered in that tag however I choose! My Angular directive ended up looking like this:

[code lang=”js”]
.directive(‘dynamic’, function($compile){
return {
restrict: ‘A’,
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(serialized) {
console.log(‘rendering photos’)
ele.html(serialized);
$compile(ele.contents())(scope);
});
}
};
})
[/code]

I used this directive in my html and inside an “ng-repeat” in order to render each of the photos.

Fluent in Spanish and JavaScript, salsa dancer, lover of material design.

Living in San Francisco, CA