Sunday, September 10, 2017

DotNet core and vuejs - a quick recipe

The problem

So you're stuck in .NET world (maybe with Sitecore or some other God forsaken creation) and you need to do some front-end code. How the hell do you get started?

The solution

Use dotnet core 2.0.0. Scaffold your application, install all the required dependencies and start the application in development mode

    $ sudo apt-get install dotnet-sdk-2.0.0
    $ dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
    $ dotnet new vue -n hello-world
    $ cd hello-world
    $ dotnet restore
    $ npm install
    $ ASPNETCORE_ENVIRONMENT="Development" dotnet run

Happy coding

Tuesday, September 5, 2017

Back to the roots: defining a jQuery plugin in TypeScript

The problem

You're so proficient in TypeScript that you decided all your code will be type-safe. All that is left is this damn jQuery plugin that won't bow to your will and you have no idea how to make all the typing information work.

The solution

This is actually quite easy! Let's start with the obvious import:

    import * as $ from 'jquery'

Next you need to decide if your plugin will take configuration options. If that is the case then you need an interface describing those options

    interface FooOptions {
      color?: string
    }

Now we need to tell TypeScript that we're going to extend the global JQuery interface with our method:

    declare global {
      interface JQuery {
        foo(options?: FooOptions): JQuery
      }
    }

The question mark after options means this is an optional parameter and if not defined then an undefined value will be passed on

All that is left is to write the plugin itself

    $.fn.foo = function(this: JQuery, options?: FooOptions) {
      const settings: FooOptions = {
        color: 'blue',
        ...options,
      }
      return this.each(function() {
        $(this).css({ 'background-color': settings.color })
      })
    }

The method signature tells TypeScript that this in this context is a jQuery object and that there can be a FooOptions passed on.

The rest is obvious: using object spread operator we get the actual set of settings and next we just go on with the plugin's body.

Happy coding!

Back to the roots: jQuery plugins and TypeScript

The problem

So you have learned TypeScript but there is no escape from jQuery. It's crawling back over and over again from the dungeons. But you'd like to write your code in TypeScript and this damn thing doesn't know your new cool method? Want to teach it a lesson? Read on!

The stage

TypeScript is great! No question about it. I use it primarily with webpack and it is a wonderful combination. One of the best things it has is the ability to merge interface definitions. That's right! You can literally extend the interface as you wish! This will come very handy when adding new methods to be called in a jQuery chain. First let's define a plugin foo in plain JavaScript.

    import * as $ from 'jquery'

    $.fn.foo = function () {
      return this.each(function() {
        $(this).css({ 'background-color': 'blue' })
      })
    }

Nothing fancy, right? Each element passed on will receive a blue background. I love blue that is why it is blue and not red.

The solution

Now for the tricky part: teaching TypeScript the definitions. I say definitions because we'll teach it first the JQuery interface by installing the @typings/jquery package

    $ npm install --save-dev '@types/jquery'

Now if you're like me and you use jQuery 3 or later with the default TypeScript configuration you will see a message saying something about Iterable. That's easy to fix. In the tsconfig.json make sure you have the lib line containing the es2015.iterable element, like so:

    "compilerOptions": {
        "lib" : [ "es2015", "es2015.iterable", "dom" ]
    }

With that out of the way let's teach TypeScript the new foo

    declare global {
      interface JQuery {
        foo (): JQuery
      }
    }

This is called declaration merging and allows for arbitrary declarations to be added to arbitrary interfaces just like in JavaScript you can add arbitrary fields to arbitrary objects. Cool, ain't it?

Also note that we use the global module. That's because if we're inside of another module then the original JQuery interface is a different one from the one defined in the global scope and things just don't go so well in that case.

Now let's use our new toy:

    $('div').foo()

Don't forget the new way of importing jQuery requires the allowSyntheticDefaultImports compiler option.

Happy coding!

Friday, September 1, 2017

Back to the roots: jQuery plugin authoring

The problem

The code you're writing using jQuery becomes unmaintainable and hard to read

The solution

Short of stop using jQuery (if that is an option you can use then go for it and use Vue.js instead) start by extracting reusable components/plugins from your code. Writing such a plugin is nothing hard - just a frame where you put your code in:

    (function($) {
      $.fn.myPlugin = function(options) {
        const defaults = {
          // TODO: add default values for options here
        }
        const settings = $.extend(defaults, options)
        // Either process each element in a loop
        return this.each(function() {
          // TODO: add the body of what the plugin should do
          // 'this' is the current element when iterating
        })
        // or operate on all elements at once using other functions
        // return this.css({ ... })
      }
    })(jQuery)

More on authoring jQuery plugins here.

With that in place you're ready to create whatever is used in multiple places like the grownups do and enjoy the pleasure of using method chaining even more!

    $('.my-component').myPlugin({ color: red });

Going Webpack!

It is said that once you go Webpack you never go back. If that's your case then you might want to create your plugins as separate modules and let Webpack put it all together nice and easy. To do that you define your plugin as a module that imports jQuery and exports nothing, then you import that module in your main application file.

    import $ from 'jquery'

    $.fn.myPlugin = function(options) {
      const defaults = {
        // TODO: add default values for options here
      }
      const settings = $.extend(defaults, options)
      // Either process each element in a loop
      return this.each(function() {
        // TODO: add the body of what the plugin should do
        // 'this' is the current element when iterating
      })
      // or operate on all elements at once using other functions
      // return this.css({ ... })
    }

In main.js

    import $ from 'jquery'
    import './my-plugin'

    $(document).ready(function() {
      // use your plugin here
      $('.my-component').myPlugin({ color: red });
    })

The easiest Webpack configuration that will work in this case is

    module.exports = {
      entry: __dirname + '/index.js',
      output: {
        path: __dirname + '/dist',
        filename: 'index.js'
      },
      devtool: 'source-map'
    }

In this case you can include the dist/index.js and you'll have all you need in one place. And that's it! Easy as a lion :)

And last but not least: don't name your plugin myPlugin. It's lame to do in anything else than a tutorial. Be explicit, use good names, be a good programmer!

Happy coding!

PS

Please be mindful that the function assigned to $.fn.myPlugin as well as the one that iterates over elements that have been passed on can't be arrow functions because jQuery sets the this element. Just remember that.