Adding Snap.svg to Vue.js and Nuxt.js Projects

SVG is amazing, and if you’re building any custom vector graphics from your client code, one of the easiest libraries to use is Snap.svg. I’ve used it in a number of projects, including vanilla JavaScript and various transpiling setups including Transcrypt.

I’m trying to go a little more mainstream after wasted years of time on fringe technologies that fell out of favor.

I’m spending a lot of time these days learning Vue.js and really hoping this is going to be a worthwhile long term investment in my skillset. So it was only a matter of time before I found myself needing to get Snap.svg working in my Vue.js projects, which meant some extra fiddling with WebPack.

Getting Snap.svg Working with Vue.js

Out of the gate, there’s some hurdles because Snap mounts itself on the browser’s window object, so if you’re trying to load Snap through WebPack (as opposed to just including it in a project using a conventional script tag), you need to do some gymnastics to get WebPack’s JavaScript loader to feed the window object into Snap’s initialization logic. You can find an overview of the problem in this GitHub issue which illustrates the obstacles in the context of using React, but the issues as they relate to Vue.js are the same.

I’m assuming you have a Vue.js webpack project that you started with vue-cli or from a template that has everything basically running okay, so you’ve already got Node and webpack and all your other infrastructure in place.

For starters, you’ll want to install Snap.svg and add it to your project dependencies, so from a terminal window open and sitting in the directory where your project’s package.json/package-lock.json sit…

npm install --save snapsvg

That will download and install a copy of the Snap.svg source into your node_modules directory and you’ll have it available for WebPack to grab.

Normally you’d be able to use a package installed like this by using an import statement somewhere, and you’d think you could do this in your Vue project’s main.js file, if you start down this path you’ll get the window undefined issue described in that GitHub link above.

The tricky bit though is getting WebPack to load the Snap properly, and to do that we’ll need a WebPack plugin that lets us load as a JavaScript dependency and pass some bindings to it. So, in that same directory install the WebPack imports-loader plugin…

npm install --savedev imports-loader

To tell the imports-loader when it needs to do its magic, we have to add it to the WebPack configuration. I changed my webpack.base.conf.js file to include the following inside the array of rules inside the module object…

 module: {
   rules: [
      ...
       {
       test: require.resolve('snapsvg'),
       use: 'imports-loader?this=>window,fix=>module.exports=0',
       },
      ...
     ]
   },

Now we can load Snap.svg in our JavaScript, but imports-loader uses the node require syntax to load the file. So in our main.js, we can attach Snap.svg by telling WebPack to invoke the exports loader like this…

const snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);

…and then attach it to our root Vue instance, still in main.js, something like this…

const vueInstance = new Vue( {
 el: '#app',
 snap,
 router,
 axios,
 store,
 template: '<App/>',
 components: { App }
} );

export { vueInstance };

There is some redundancy in that require() call and the way we setup the module resolution in the WebPack configuration. I’m fuzzy about why I seemed to need this in both spots, but it works so I’m running with it. If you have insights they’d be appreciated; let me know in the comments.

Getting Snap.svg Working with nuxt.js

Nuxt requires a slightly different twist, because as you’re aware a typical Nuxt project doesn’t have either a main.js file or a native copy of the WebPack configuration. We need to make the same changes, but just in a slightly different spots.

You need to install both snapsvg and imports-loader just like we did above…

npm install --save snapsvg
npm install --savedev imports-loader

The way we modify the WebPack configuration in a Nuxt project is to create a function that accepts and extends the WebPack configuration from with your nuxt.config.js file…

/*
 ** Build configuration
 */

 build: {
   extend(config, ctx) {
      config.module.rules.push( {
        test: require.resolve('snapsvg'),
        use: 'imports-loader?this=>window,fix=>module.exports=0',
       } );
     }
   }

Since we don’t have a main.js, we need to use a Vue.js plugin to inject shared objects and code into Vue. In your projects plugins folder, create a file named snap.js that contains code to attach a snap object created again using imports-loader…

export default ({ app }, inject) => {
  app.snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);
}

…and back in your nuxt.config.js file, include this plugin…

plugins: [
   ...
   {src: '~/plugins/snap'},
   ...
],

These approaches seem to work well for me in both a standard Vue.js and Nuxt.js projects, but both of these setups have been cobbled together from reading a lot of other bits and pieces… If you’ve got a better or approach or see a way to clean up what I’ve done, please let me know.

Meanwhile, good luck with your Snap and Vue projects!

 

 

 

 

Migrating from Dasher.tv to the AnyBoard App

For a little over a year, I’ve been running a fantastic NOC-style dashboard on the AppleTV in my office courtesy of a nifty app called Dasher. It took a little Python gymnastics, but I was able to pull data from Google Analytics, Ahrefs and Staq to assemble a consolidated view of what’s happening at DadsWorksheets.com, all of which helps keep my eye on getting things done there.

Much of the work in this is a Python script that runs locally to collect the data. I’ve been pushing that data up to Dasher’s servers, which then gets fed back to the Dasher app on the AppleTV. But I’ve been concerned for quite some time because this app never got the love or attention it deserved, I’m sure in large part because it required chattering through a web API to push the data. So as well as this worked, it was never really going to be broadly adopted by anyone but us propeller heads.

That means I knew the hammer was going to fall on this little gem at some point, and sure enough I got the email yesterday that Dasher was shutting down in May.

I rely on this dashboard enough that Dasher’s demise caused me to peel off for part of yesterday to find a replacement. I didn’t want to spend the next few weeks with some thought gnawing the back of my head, so I at least needed a plan.

And there are several good alternative dashboard apps out there, many of which with integrations to Analytics plus 100 other services I didn’t need. These are all great solutions, but they also all came with $10 per month fees, and missing integrations to oddball places like Staq and some of the other custom bits that I’d still have to jump through hoops to get fed anyway.

I’m already collecting all the data and generating a few simple charts in Pillow, so sending it somewhere that would ultimately show up on the AppleTV shouldn’t be hard. If there was even a simple version of Safari or another browser I could load on the AppleTV to bring up and auto-refresh a web page, I’d have a solution by kicking out some HTML or a even a full blown Vue.js app, but short of renewing my Apple developer account, reinstalling Xcode and side-loading tvOSBroswer, there isn’t much on the map.

That’s why I’m so glad I found AnyBoard. This is a great little app that does everything and more that Dasher did, but without putting a third party server in the middle.

When you setup Anyboard, you point it at a JSON file that you’ve made visible somewhere. That JSON file describes how one or more dashboards are laid out and also where to get the data. The data comes from other JSON files you identify using URLs in the configuration. By refreshing those JSON files with new data, the Anyboard app will have access to live data feeds from whatever sources you can cobble together. There’s also a pre-built setup for Nagios, but I didn’t play with it here.

Because all of the dashboard data is moving between the Apple TV and the local network, you can configure Anyboard to hit URLs on a local server, so your dashboard configuration and your actual data can stay inside the building. Also, you’re not dependent on a third party developer necessarily pulling the plug on the API that feeds the app. So I’m anticipating a very long relationship with Anyboard here.

Not that I think there’s anything to worry about. I traded a few emails with Ladislav at sfĂ©ra, the Anyboard developer, and he was eager to help work through some odd things I was doing in my configuration and answer a few questions I had. These are the kinds of guys worth our support.

I was able to port my Dasher configuration over to Anyboard in about half a day, and the resulting dashboards look better than they ever have. Anyboard is free, there’s no premium version (which I would have gladly bought) or subscription fees (which I would have ruled out). It’s a solid app that does an important job and does it well. I can see a few minor areas that I hope Ladislav polishes up in future builds, but if you’re comfortable cranking out a few simple JSON files, I can definitely recommend Anyboard as an AppleTV dashboard solution without hesitation.

You can find out more about Anyboard at https://anyboard.io/.