read

In a previous article, we looked at making social buttons in vanilla JS, and getting share/tweet counts from social APIs. But using vanilla JS can lead to a mess of code that is difficult to maintain.

In this article, we'll turn the previous example into a Meteor package. Why? This way it can be shared easily, configured and dropped on the page as a simple component. In the end, your component should look something like this:

See a working example here.

These buttons will open up a new window passing in your sites url & title to the social media provider. Developers simply add the component to the page.

{{> social_buttons}}

Optionally, a developer can configure the buttons by calling a function anywhere in the code:

SocialButtons.config({
  useCount: true,
  incrementing: false,
});

Or pass in custom url's or text messages:

{{> social_buttons url=url text=title}}

Have a look at working example on MeteorPad.

We'll walk through:

  1. Setting up a Meteor package
  2. Some package development surprises
  3. Configuring a Meteor package
  4. Publishing your package

1. Package Setup

Begin by settings up a development environment to test your package. I'll call my test app "socialButtonsTest"

meteor create socialButtonsTest

Make your folder structure look something like the following:

.meteor/
client/
  \- config.js  (configuration here)
  \- index.html (component here)
packages/

Go into the packages directory and create your package. Packages are name spaced, in this case my package is called shmck:meteor-social-buttons.

cd socialButtonsTest
cd packages
meteor create --package yourId:yourPackageName

This will create a basic setup for your package in package.js.

Package.describe({
  name: 'shmck:meteor-social-buttons',
  version: '0.0.1',
  summary: '',
  git: '',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.1.0.3');
  api.addFiles('social-buttons.js');
});

We won't go over every step in creating a package, but I'll try to cover a few surprises.

2. Package Development Surprises

A Meteor package doesn't work in the same way as a Meteor app. There are a few surprises.

Globals are okay

No joke. Within a package, global variables are what you use to communicate between files. For example, here I've created a global 'socialData' and placed it in its own file, 'data.js'.

socialData = {
  facebook: {
    link: '//www.facebook.com/sharer/sharer.php',
    classNames: 'fa fa-facebook',
    name: 'facebook',
    openWindow: function (url, text) {
      window.open('//www.facebook.com/sharer/sharer.php?u=' + url + '&t=' + text,
        this.name,
'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=300,width=600');
    },
    measure: 'shares',
    countAPI: 'http://graph.facebook.com/?id='
  },
  twitter: {
    link: '//twitter.com/share',
    classNames: 'fa fa-twitter',
    name: 'twitter',
    openWindow: function (url, text) {
      window.open('//twitter.com/share?text=' + text,
        this.name,     
'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=255, width=550');
},
    measure: 'count',
    countAPI: 'http://cdn.api.twitter.com/1/urls/count.json?url='
  }
}

Globals in a package won't leak outside of the package. I could have placed everything within the same file, but globals in Meteor allow you to break your package into smaller files.

If you wish to expand a global variable beyond your package, you can use api.export(). In this case, I want SocialButtons containing necessary component data to be placed on the global scope.

Package.onUse(function (api) {
    api.export('SocialButtons', 'client');
});

Now SocialButtons is available globally on the client-side. I could use it load the SocialButtons.buttons data & SocialButtons.config() to load user configurations.

Load order is important

Generally in Meteor, load order is handled automatically. This is not so with packages. Files will be loaded in the order you specify. In this case, I'm loading my html file first, otherwise I will get a 'template unavailable' error.

Package.onUse(function (api) {
 api.addFiles([
    'lib/social-buttons.html',
    'lib/defaults.js',
    'lib/data.js',
    'lib/functions.js',
    'lib/social-buttons.js',
    'lib/social-buttons-template.js',
    'lib/social-buttons.css'
  ], 'client');
});

Folder conventions don't hold true in packages. If you want a file to run on the 'client' or 'server' only, you must specify that when you add your file.

Meteor isn't built into your package

If you want to load Meteor in your package, you must specify it as a dependency. For example:

Package.onUse(function (api) {
  api.use('meteor', 'ddp');
});

In the case of my package, I'm only using two shared packages from Meteor: 'templating' and 'underscore'. I've also added 'font-awesome' for styles.

Package.onUse(function (api) {
  api.use(['templating', 'fortawesome:fontawesome', 'underscore'], 'client');
});

3. Configuration

There are a few steps involved in setting up user created configurations.

Add Defaults

First we should setup our defaults.

defaults = {
  facebook: true,
  twitter: true,
  google: true,
  pinterest: false,
  delicious: false,
  tumblr: false,
  pocket: false,
  reddit: false,
  linkedin: false,
  wordpress: false,
  hackernews: false,
  stumbledupon: false,
  mail: false,

  incrementing: false,
  useCount: false,
  incrementerSpeed: 150,
  via: '' // enabled if a string is entered
};
Configure Custom Options

It's quite easy to setup custom configurations that overrides the defaults.

SocialButtons = {
  options: {},

  config: function (options) {
      // add options
  }
};

Here, SocialButtons.config() will add the custom options into a new object.

Validate Configurations

Be sure to validate your keys to prevent errors. Here I'm only adding options which are listed as keys in the defaults object.

config: function (options) {
  var validKeys = _.keys(defaults);
  _.keys(options).forEach(function (key) {
    if (_.indexOf(validKeys, key) >= 0) {
      SocialButtons.options[key] = options[key];
    } else {
      throw key + ' is not a valid SocialButtons key.';
    }
  });
}
Override Defaults with Custom Options

With Underscore, you can use _.extend(obj, overrideObj, anotherOverrideObj). As Underscore comes built in with Meteor, I've continued with it. If you're using Lodash, try _.assign(obj, overrideObj).

SocialButtons.settings = _.extend(defaults, SocialButtons.options);

We now have a settings object with the options we will use in our app.

Load Settings on Startup

User configurations can be placed anywhere in the app; we now have a loading time issue. Meteor runs packages first, but we need configurations to start after all code has been read.

You can fix this by wrapping your app in a Meteor.startup function that will run after configurations have been set.

SocialButtons {...}

Meteor.startup(function () {
   SocialButtons.settings = _.extend(defaults, SocialButtons.options);

  // use settings to run package here

});

4. Package Publishing

Once everything is ready, publishing your package is easy.

Specific Versions

Just one last thing. Any packages that aren't part of Meteor's core need a version number to prevent breaking changes. I used 'fontawesome', so I'll have to set it's current version.

api.use(['templating', 'fortawesome:fontawesome@4.4.0', 'underscore'], 'client');
Package.describe

Once you publish on Atmosphere there is no going back. Your package will be in the system forever, you can only add improved versions on top. Be careful! Make sure your Package.describe() is filled out fully

 Package.describe({
  name: 'shmck:meteor-social-buttons',
  version: '0.1.1',
  author: 'Shawn McKay',
  summary: 'Social Icon Buttons for Meteor',
  git: 'https://github.com/ShMcK/Social-Icon-Buttons',
  documentation: 'README.md'
});

Great, now time to publish. Go into the specific package and run a few commands.

meteor publish --create

This will publish your package to AtmosphereJS. you can find it there after a short period of time, or by typing meteor search yourPackageName in the console.

Keep in mind that a package cannot be deleted once published. It can only be removed from the search results using the following command:

meteor admin set-unmigrated YOURPACKAGE

Make packages, but be careful not to pollute the meteor package system with unnecessary or duplicate packages.

5. Updating

To update your package, enter into your package folder and increment your package version number.

Package.describe({
  name: 'shmck:meteor-social-buttons',
  version: '0.1.2',
  ...
});

Keep in mind that meteor versions don't support -alpha or -beta versioning. You can see my mistake in the shmck:angular2 package versioning. You can, however use 0.1.1_1, 0.1.1_2 type versioning.

Aftewards, publish your package.

meteor publish

You may also want to consider using some easy bash scripts for versioning and publishing.

Conclusion

If you've created components for your own apps, I encourage you to add them to Meteor's package system. In the long run, the more people contribute, the more it will come back to reward you as well. Help build bridges for those behind you and they will follow.

You can find the 'shmck:meteor-social-buttons' package on Github or AtmosphereJS. If there is another social share button you'd like added, send me a link to the API and I'll add it.

Blog Logo

Shawn McKay

Published

Image

ShMcK

JavaScript Web Dev

Back Home