Thursday, June 22, 2017

Using KnockoutJS in the new SPFx - containerless control flow removed

Many of you probably know how excited and happy I am with the SharePoint Framework (aka SPFx). Finally, a worthy client side framework for SharePoint extensibility.

SPFx promises a lot, and delivers even more with a very powerful engine that drives it.

One of the things it promises is giving developers the freedom to choose their client side development story, which platforms frameworks and libraries they want to use and how.

However, it is clear that ReactJS is the framework of choice for Microsoft, having the most complete Office Fabric components library (and probably the only one that is maintained by Microsoft pretty regularly), so by all means: when ever you can go react - don't look back.

While building our DataViewPlus web part (DVP), I had a requirement that prevented me from using react.
You see, react uses controls that are compiled into JavaScript objects, thus limiting the rendered HTML to what your developer had when he built the web part.

In our DVP web part, we wanted to give users the ability to customize, change, extend the html of the rendering templates or even provide their own HTML template for the rendered web part. Similar to what you could do with the SharePoint Designer data view, only without the nasty xsl language.

For that reason, I think KnockoutJS (KO) was the perfect fit for my project.

One of the features in KO that I love using is container-less commands. So, unlike other MVVM frameworks that had to output a tag to the page in order to do bindings - KO allows you to use HTML comments to do the bindings.
For example:
<!-- ko if: someExpressionGoesHere -->

I want to make this item present/absent dynamically

<!-- /ko -->

See, this is very useful in KO and I use it a lot. I think you can't really do much with KO without using at least some containerless statements. Once you grow beyond your hello world project, you will pretty quickly find yourself using a containerless if or foreach statement.

I noticed during my development stages with SPFx, using the workbench I had no problems with it at all and my web part worked perfectly.

However as soon as I built to production (gulp --ship) I noticed none of my KO templates rendered to the page.

After a bit of digging (and a lot of console.log...) I noticed all my KO HTML templates were loaded correctly with one small difference: All my HTML comments were removed!

It seems during production build, the html-loader plugin kicks in and calls UglifyjsWebpackPlugin.
This plugin by default remove all comments from the output, except important comments that contains:
/*!, /**!, @preserve or @license

Luckily, SPFx allows us to intervene with its build steps and make some changes in the way webpack works. (More on webpack in a future post)
There are several ways we can go about fixing this issue with the comments.
One is to replace the html-loader that SPFx uses with a KO friendly version.
Another would be to edit the configuration of the UglifyjsWebpackPlugin in the node_modules folder, which I don't recommend since you will have to re-do it every time you do npm install on a new machine.

So I dug deep into its code and found that the loader is checking for parameters in a query string format provided to it, and one of these parameters that we can pass is called "removeComments"!

So, by editing my gulpfile.js I was able to tell SPFx not to remove any comments from HTML template files I was loading.

Here is my new gulpfile.js:
'use strict'; const gulp = require('gulp'); const gutil = require('gulp-util'); const build = require('@microsoft/sp-build-web'); build.configureWebpack.mergeConfig({ additionalConfiguration: (config) => { config.module.loaders.forEach((loader) => { if (loader.loader === "html-loader") { gutil.log("Got html loader " + JSON.stringify(loader)); loader.loader += "?removeComments=false"; } }); return config; } }); build.initialize(gulp);
I must note that it should also be possible to configure this to only keep KO comments, but that would require changing the way the loader is configured which might be more tricky (if not impossible) via the gulpfile.js

So, my conclusion is, although the guys @MSFT did an amazing job with SPFx and opening it to other frameworks, and while KO is actually included in the yeoman generator - there is still an advantage on working with ReactJS over KO or other frameworks, simply because that's what the developers are using so it makes sense it is more stable and tested more.
However, like you just saw, this framework is powerful enough that even when you need to use other less popular frameworks you can still make do and apply some tweaks and fixes yourself as needed.

Hope this helps you guys if you happen to choose KO for your SPFx web parts.

Please leave a comment and let me know which framework you are using with SPFx, and what is your experience with these platforms.

No comments: