Angular2 + Electron + Yeoman + SASS + Bootstrap: A complete guide to setting up a new desktop app

UPDATE: Since the Angular team updated ng2 from beta to RC, there were changes which break the step-by-step tutorial described in this post. I’ll update it as soon as possible.


As a single developer, creating and developing a big application such as Slidebean on your own is no easy task. In comes Angular to save the day. As the main framework for Slidebean, Angular — not without its kinks here and there — has exceptionally performed and exceeded my expectations as an application framework.

For those who don’t know, Slidebean let’s you create incredible presentations in an easier, faster, better way than with Powerpoint. Check it out at slidebean.com

That being said, Angular 2 is coming. And as much as Angular 1 was a groundbreaking framework, ng2 — as the cool kids call it — comes with big improvements (and big changes!) across the board. Alas, it’s time to move on.

The following is a guide I created after spending a lot of time finding out how to properly set up a project aimed for production which included:

Continue reading

Dealing with ng-style and vendor prefixes

Slidebean is an app that lets you create beautiful presentations in minutes. It transforms your content into slides automatically, all thanks to the magic of AngularJS.

Most of the slide styles Slidebean uses are prebuilt in regular CSS files. But, because of the dynamic nature of app, it applies colors and other CSS properties to slides using ng-style and javascript as well. It works great, but I ran into a small issue regarding vendor prefixes that I wanted to share.

For one of our templates, we wanted to have a radial gradient as the background of the slides. In regular CSS, you would do something like this to support different browsers:

.slide {
  background: -webkit-radial-gradient(#FFF, #DDD);
  background:    -moz-radial-gradient(#FFF, #DDD);
  background:         radial-gradient(#FFF, #DDD);
}

Thing is, Slidebean doesn’t know the colors until runtime. Instead of using a CSS file, I used Angular‘s ng-style. The slide HTML looks something like this:

<section class="slide" ng-style="getSlideStyles()">...</section>

with a function getSlideStyles that returns the CSS styles:

scope.getSlideStyles = function() {
  var colorA = "#FFF";
  var colorB = "#DDD";
  var styles = {};
  styles['background'] = '-webkit-radial-gradient(' + colorA + ',' + colorB + ')';
  styles['background'] = 'moz-radial-gradient(' + colorA + ',' + colorB + ')';
  styles['background'] = 'radial-gradient(' + colorA + ',' + colorB + ')';
  return styles;
}

All set right? Wrong. In the end, styles ends up with only one property, ‘background’, with a value of ‘radial-gradient(#FFF, #DDD)’, because there can only be one key named ‘background’ in the styles object.

Without looking at the source code of ng-style, I thought maybe it supported an array of values per property. So the styles object could be rewritten as:

scope.getSlideStyles = function() {
  var colorA = "#FFF";
  var colorB = "#DDD";
  var styles = {};
  styles['background'] = [
    '-webkit-radial-gradient(' + colorA + ',' + colorB + ')',
        'moz-radial-gradient(' + colorA + ',' + colorB + ')',
            'radial-gradient(' + colorA + ',' + colorB + ')'
  ];
  return styles;
}

Sadly, though, ng-style does not support this. I ended up creating an ng-style clone directive of my own, renamed sb-style. Here’s the original ng-style function as of this post:

scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
  if (oldStyles &amp;&amp; (newStyles !== oldStyles)) {
    forEach(oldStyles, function(val, style) { element.css(style, '');});
  }
  if (newStyles) element.css(newStyles);
}, true);

And here’s sb-style, which supports an array of values per CSS property:

scope.$watch(attrs.sbStyle, function(newStyles, oldStyles) {
  if (oldStyles &amp;&amp; (newStyles !== oldStyles)) {
    forEach(oldStyles, function(val, style) { element.css(style, '');});
  }
  if (newStyles) {
    forEach(newStyles, function(val, style) {
      if (val instanceof Array) {
        forEach(val, function(innerVal) {
          // Here's where the magic happens, thanks to jquery
          element.css(style, innerVal);
        });
      }
      else
        element.css(style, val); // Do the normal thing
    });
   }
 }, true);

To explain a bit more: The reason why this works is because jquery’s css function lets you repeat any CSS property with different values (kinda like you do in CSS), letting the browser decide which one to use.

And so, we had our background with radial gradient 🙂

What do you think: have you run into this issue? Do you think there’s a better way of dealing with this? Let me know in the comments.

5 Tips for Using Parse with AngularJS

I’m currently working a brand new project called Slidebean, which I’m a co-founder of. Slidebean lets you create stunning presentations in seconds. Check it out, it’s free! For Slidebean we decided to use two of the hottest app frameworks and components out there: AngularJS as our javascript MVC framework, and Parse as our back-end and cloud storage solution. If you haven’t heard about them, I highly encourage you to do so now, as they’re becoming more and more popular every day. There are few kinks here and there when it comes to using Parse with AngularJS. These are few tips I’ve discovered along the way to iron them out. Hope it saves you some time.

1. Define getters and setters for each of your Parse class fields

Parse lets you get and set custom fields in your classes using – you guessed it – the get and set methods. On Slidebean, users create Presentations, which have a title property. So let’s say we have a presentation variable in a controller’s $scope, and we want to display its title. We would do something like this on our html view:

&lt;div&gt;{{ presentation.get(&quot;title&quot;) }}&lt;/div&gt;

AngularJS, however, uses plain javascript object properties to read and update the values. So if we wanted to update the presentation title using an input field, the ng-model directive is not compatible:


&lt;input type=&quot;text&quot; ng-model=&quot;presentation.set('title')&quot;&gt;
&lt;!-- This doesn't make sense, but just for the sake of the example: --&gt;

One approach I read about here, is to forget about using the Parse Javascript SDK and rely on their REST API to handle models. But there’s another much simpler solution, and that is to define javascript getters and setters for each of your Parse classes. So using AngularJS, we use factory to define a model class called Presentation. The trick is that, inside, we also define a getter and setter per property. In this example, we only have one called title:


angular.module('SlidebeanModels').
  factory('Presentation', function() {

    var Presentation = Parse.Object.extend(&quot;Presentation&quot;, {
      // Instance methods
    }, {
      // Class methods
    }
    });

    // Title property
    Object.defineProperty(Presentation.prototype, &quot;title&quot;, {
      get: function() {
        return this.get(&quot;title&quot;);
      },
      set: function(aValue) {
        this.set(&quot;title&quot;, aValue);
      }
    });

    return Presentation;
  });

Now, we have a model class that works seamlessly with ng-model. And so, to update the presentation title, we just do this:


&lt;input type=&quot;text&quot; ng-model=&quot;presentation.title&quot;&gt;

Pretty neat!

UPDATE

As noted by Leo Pelekh in the comments (thanks Leo!), if you wish to add a getter for a property that contains another Parse Object so that it returns your registered class, you can. You just need to make sure you have included the other class as a dependency.

Say you have a Comment class and a Post class, and the Comment has a property called “parentPost”, which is a pointer to the Post the Comment belongs to. If you add a getter to Comment for “parentPost” without including the class Post as a dependency of Comment, then the getter would return a plain Parse.Object instance. On the other hand, if you had included Post as a dependency, the getter will return a instance of Post.

This trick is similar to what’s mentioned in point #3 about the custom User class.

2. Store your current Parse user in the $rootScope

I’ve read that you should almost never set variables in your AngularJS app $rootScope. Parse provides the very convenient Parse.User.current() method, which returns the current active session user. Thing is, changes made to this user variable happen outside of the AngularJS world, and therefore are hard to keep track of and be digested by Angular. I find it convenient, then, to store the current Parse user in a $rootScope variable called sessionUser (or whatever you wish to call it), thus making it accessible to every controller $scope. This var is initialized right after the AngularJS app starts running:


angular.module('SlidebeanApp')
  .config(function ($routeProvider, $locationProvider) {
    // Config goes here
  })
  .run(function($rootScope) {

    Parse.initialize(&quot;parse app&quot;, &quot;parse credentials&quot;);

    $rootScope.sessionUser = Parse.User.current();

  });

To keep things in order, we created a singleton called SessionService, and we made it so that this is the only place where $rootScope.sessionUser is manipulated. This service handles log-ins and log-outs, and updates the variable accordingly. For example, a navigation bar controller that displays the current user can just react to this var, and modify its looks and functionality accordingly:


&lt;!-- Show Log In and Sign Up buttons when there's no session --&gt;
&lt;ul ng-show=&quot;sessionUser == null&quot;&gt;
  &lt;li&gt;&lt;button type=&quot;button&quot; ng-click=&quot;logIn()&quot;&gt;Log In&lt;/button&gt;&lt;/li&gt;
  &lt;li&gt;&lt;button type=&quot;button&quot; ng-click=&quot;signUp()&quot;&gt;Sign Up&lt;/button&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- Display the current user when there's a session --&gt;
&lt;ul ng-show=&quot;sessionUser != null&quot;&gt;
  &lt;li class=&quot;dropdown&quot;&gt;
    &lt;a href=&quot;#&quot; class=&quot;dropdown-toggle&quot; data-toggle=&quot;dropdown&quot;&gt;
      {{ sessionUser.name }}
      &lt;b class=&quot;caret&quot;&gt;&lt;/b&gt;
    &lt;/a&gt;
    &lt;ul class=&quot;dropdown-menu&quot;&gt;
      &lt;li&gt;&lt;a ng-href=&quot;/profile&quot;&gt;Profile&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a ng-href=&quot;#&quot; ng-click=&quot;signOut()&quot;&gt;Sign Out&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

3. Extend Parse.User and include it from the get-go

I found the documentation for extending Parse.User not clear. But basically, if you want to extend the base Parse.User class, it’s just as easy as extending any other Parse.Object. The caveat is: make sure you include your custom class before calling Parse.User.current(), so that you receive an instance of your class. In Slidebean, we wanted to have a custom getImage function for users, where we could abstract away the complexity of retrieving the user’s avatar either from Facebook or from Gravatar. So we extended Parse.User like this:


angular.module('SlidebeanModels').
  factory('SlidebeanUser', function() {

    var User = Parse.User.extend({

      getImage : function() {
        // return the appropriate facebook image url or gravatar image url
      }

    }, {
      // Class methods
    });

    return User;
  });

Then, we just made sure to include our SlidebeanUser class as a dependency for the app’s run method:


.run(function($rootScope, $location, SlidebeanUser) {
  Parse.initialize(&quot;parse app&quot;, &quot;parse credentials&quot;);

  // This is now an instance of our SlidebeanUser class 🙂
  $rootScope.sessionUser = SlidebeanUser.current();

  // and this would work (if there's a user signed in, of course):
  var imageUrl = $rootScope.sessionUser.getImage();
})

4. Delay the Facebook SDK initialization until your AngularJS app has begun running

If you allow users to sign in using Facebook to your app, it’s a good idea to load Facebook SDK until after you have initialized Parse. And it’s a good idea to initialize Parse after your AngularJS app begins running. In general, then, it’s a good idea to have all your application initialization code in one place, and a good place to do that is in your AngularJS app’s run method. In our Slidebean app (and I’d suspect in most apps), the initialization order goes like this:

  1. AngularJS
  2. Parse
  3. Facebook

and to achieve this, our app run method looks like this. Notice how the Facebook SDK async load is placed in here, instead of placed somewhere in the HTML body where it’s normally suggested to.


.run(function($rootScope, $location, SlidebeanUser) {

  // 1) AngularJS app is now running

  // 2) Initialize Parse and set the current user to the $rootScope
  Parse.initialize(&quot;parse app&quot;, &quot;parse credentials&quot;);

  $rootScope.sessionUser = SlidebeanUser.current();

  // 3) Finally, init Facebook
  window.fbAsyncInit = function() {
    Parse.FacebookUtils.init({
      appId: 'facebook app id',
      channelUrl : '//www.slidebean.com/fbchannel.html',
      status: true,
      cookie: true,
      xfbml: true
    });
  };
  (function(d, s, id){
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) {return;}
    js = d.createElement(s); js.id = id;
    js.src = &quot;//connect.facebook.net/en_US/all.js&quot;;
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));
});

Don’t forget to add this in your index.html though, which the Facebook SDK needs:


&lt;div id=&quot;fb-root&quot;&gt;&lt;/div&gt;

5. Wrap Parse async calls inside $q promises

You’ll find that it’s a good idea to wrap Parse asynchronous calls inside AngularJS promises, instead of just performing Parse queries directly in your models or controllers. One of the benefits is that your data changes will be digested by AngularJS automatically. Let’s say on Slidebean we wanted to retrieve all the presentations belonging to the current user. We first defined the Presentation model class and added a static method to retrieve presentations by a given owner. Inside this method, we wrap the async query using AngularJS’ $q promises:


angular.module('SlidebeanModels').
  factory('Presentation', function($q) {

    var Presentation = Parse.Object.extend(&quot;Presentation&quot;, {
      // Instance methods
    }, {
      // Class methods

      listByUser : function(aUser) {
        var defer = $q.defer();

        var query = new Parse.Query(this);
        query.equalTo(&quot;owner&quot;, aUser);
        query.find({
          success : function(aPresentations) {
            defer.resolve(aPresentations);
          },
          error : function(aError) {
            defer.reject(aError);
          }
        });

        return defer.promise;
      }
    });

    // Properties
    Object.defineProperty(Presentation.prototype, &quot;owner&quot;, {
      get: function() {
        return this.get(&quot;owner&quot;);
      },
      set: function(aValue) {
        this.set(&quot;owner&quot;, aValue);
      }
    });
    Object.defineProperty(Presentation.prototype, &quot;title&quot;, {
      get: function() {
        return this.get(&quot;title&quot;);
      },
      set: function(aValue) {
        this.set(&quot;title&quot;, aValue);
      }
    });

 return Presentation;
 });

Then, inside of any given controller, if we want to fetch the presentations, we would do something like this:


angular.module('SlidebeanApp')
  .controller('DashboardCtrl', function($scope, Presentation) {

    Presentation.listByUser($scope.sessionUser).then(function(aPresentations) {
      $scope.presentations = aPresentations;
    }, function(aError) {
      // Something went wrong, handle the error
    });
  });

And that’s it. Now the list of presentations can be displayed in HTML using ng-repeat. ===== That’s a wrap, for now. If you have any more tips, by all means post them in the comments and I’ll keep adding them in the post. Feel free to post any questions too. Happy coding ^___^

Fire.fm

This post is about Fire.fm and how it came to be. I no longer support it, unfortunately. Happy reading 😀

“What if you could play music using Firefox and nothing more?” That was the question that sparked the creation of Fire.fm.

Back in March 2008, my friend Jorge Villalobos spotted the announcement of the Extend Firefox 3 contest, sponsored by Mozilla to promote the launch of Firefox 3. This is usually an annual event, but at the time I personally had never heard of it. Me and Jorge, we both already had considerable experience in Firefox development, so he asked me if I were interested in participating together. I was definitely interested, yet we had no concrete ideas. Normally, the contest has two categories: best new add-on and best updated add-on; this time, however, they had opened two additional categories: best shopping add-on and best music add-on. We were both immediately drawn to the latter, since we both love music (well, who doesn’t?). We saw this as a great opportunity to develop something both useful and cool.

First we did our homework. A quick search for existing music related add-ons yielded almost nothing. FoxyTunes was the most popular music add-on at that time; it inserted a few buttons and controls in Firefox to let you control iTunes remotely. I remember I liked the idea of having the integrated playback controls in Firefox, but I didn’t like the dependency of having iTunes running in the background for it to work.

One of the contest sponsors, specifically of the music category, was Last.fm, an Internet radio station and music community website. At the time, I had never really used their site, but because of the contest and our research, I started using it a lot and ended up loving it. In Last.fm, you start by typing in the name of an artist you like; it then plays music from that artist and from similar artists you may or may not know of. This is a great way of discovering new music, but one grievance was that you had to have the Last.fm website opened all the while you were listening to the music. It all fell quickly into place: “What if we can do something like FoxyTunes, but using Last.fm instead of iTunes?”, we thought. The idea of listening to music without the need of anything other than Firefox was very exciting for us.

And so we began. But before we could though, we had to overcome two major potential showstoppers. First, we had to come up with a way of playing music; HTML5 was not coming to Firefox until a couple of years later, so Flash was our only alternative. Second, we had to find out if we could, somehow, mimic what the Last.fm in-site Flash player was doing, i.e. how it was requesting the music files from the Last.fm servers. In retrospective, this was, without a doubt, the most complex part of the whole implementation of the add-on; once we got those out of the way, the rest was pretty straightforward.

We put much emphasis in the design of the user interface, as we were convinced we wanted it to be as minimal and unobtrusive as possible. We also wanted to make sure it complied with accessibility standards and with each operating system’s look and feel. And we also wanted the interface to feel easy and intuitive to use.

After a month and a half of development (in our spare time!), and half a month of debugging, we submitted our add-on to the Extend Firefox 3 contest. We waited for the results anxiously, since we really felt our add-on was a serious contender. Finally, on August 21st, 2008, the winners were announced, and Fire.fm won the best music add-on category. As part of the competition prize, we flew to London and met with the nice folks at the Last.fm headquarters.

Initially a world-wide free service, Last.fm is now only free in the US, UK and Germany, and a paid subscription service everywhere else. This, unfortunately, might change even further in the near future. Fire.fm, as an add-on itself, is and always will be completely free; we only accept donations, which allow us to continue supporting it. We add new features from time to time, and we try to help out all users who have trouble with the add-on in a timely manner.

Fire.fm had seen almost a whopping 4 million downloads, and had an active user base of ~300,000 users during its peak.