Simple custom Angular (Angular2) Pipe that extends Number

We have a lot of code that looks like this in our new Angular (Angular2) module:

<div style="width:210px">
    <div>
        {{ (overviewDetails.TotalTurnoverActualSecured ?
      (overviewDetails.TotalTurnoverActualSecured / 1000000) : 0) | number:'1.2-2' }}
    </div>
    <div>{{ overviewDetails.TotalTurnoverActualSP / 1000000 
      | number:'1.2-2' }}</div>
    <div>{{ overviewDetails.TotalTurnoverActualSPF / 1000000 
      | number:'1.2-2' }}</div>
    <div style="clear:both"></div>
</div>

The main reason is that we just want to display numbers in 1.2 million, not 1200,000.

So obviously, we think about wrapping this in a custom Pipe function.

number is a Pipe name for the DecimalPipe class.  So we can extend that and borrow all the work the DecimalPipe does with parsing and formatting.

import { Pipe } from '@angular/core';
import { DecimalPipe } from '@angular/common';

@Pipe({
  name: 'million'
})
export class MillionPipe extends DecimalPipe {
  transform(value: number): any {
    return super.transform(((value || 0)/ 1000000), "1.2-2");  
  }
}

The Angular2 template then simplifies down to:

<div style="width:210px">
    <div>{{ overviewDetails.TotalTurnoverActualSecured | million }}</div>
    <div>{{ overviewDetails.TotalTurnoverActualSP  | million }}</div>
    <div>{{ overviewDetails.TotalTurnoverActualSPF  | million }}</div>
    <div style="clear:both"></div>
</div>

It is so nice to work with a Proper Object-Oriented language like TypeScript here.  Look at that extends.  <3

 

Notes on KnockoutJS Mapping and ViewModel style guide

I wanted to write down some of the better practices I've settled with since using KnockoutJS for nearly two years now.  A lot of these has to do with just avoiding errors that I see later on, as well as working with the peculiarities of the Knockout Mapping library.

These are not definitive, so let me know if you REALLY disagree!

function PersonViewModel(data) {
  var self = this;
  
  self.firstName = ko.observable();
  self.lastName = ko.observable();
    
  ko.mapping.fromJS(data, {}, self);

  self.name = ko.computed(function(){
    return ko.unwrap(self.firstName) + " " + ko.unwrap(self.lastName);
  });
}

Class Definition

  • Define a class, I always name the class CapitalizedCase, as opposed to camelCase for variables.  Makes it clear when I'm about to instantiate a variable
  • I also always name ViewModel separately from just normal classes.  ViewModel are special, they are for data-binding.  To me, PersonViewModel is different from Person.

You can now create a view model like this.

var personVM = new PersonViewModel();

You can also define a class this way

var PersonViewModel = function(data){
  var self = this;
  
  // ...
};
  • But if you do it this way, understand that Javascript Hoisting is now in effect, and the class is not available until this line.
  • Also, remember the trailing semi-colon if you go for this assign variable syntax.

Property Definition & Knockout Mapping

ko.mapping.fromJS can be used to instantiate properties as a constructor.  But there are potential issues.  Consider:

function PersonViewModel(data) {
  var self = this;
  
  //self.firstName = ko.observable();
  //self.lastName = ko.observable();
  //if we don't define these...
    
  ko.mapping.fromJS(data, {}, self);

  self.name = ko.computed(function(){
    return ko.unwrap(self.firstName) + " " + ko.unwrap(self.lastName);
  });
}

var johnVM = new PersonViewModel( { firstName: "John", lastName: "Liu" });
var marcVM = new PersonViewModel( { firstName: "Marc" });
  • In marcVM, self.lastName is not created by ko.mapping and does not exist, and some binding statements will fail
  • So always declare (annoying) all the observable properties that you need.  Don't trust some REST response to always return data that you need.

Knockout Mapping Definition

I go with this style for defining Knockout Mapping definitions.

function PersonViewModel(data) {
  var self = this;
  self.firstName = ko.observable();
  //...
}
PersonViewModel.mapping = {
  create: function(options){
    return new PersonViewModel(options.data);
  },
  key: function(data) {
    return ko.unwrap(data.firstName);
  }
};
  • The reason is that as you'll see in next example, you need the mapping from time to time, might as well keep it in one place that you can access easily.
  • I have also seen people put mapping on ViewModel.prototype.mapping - the benefit I see with this is that within the class, you can refer to it via self.mapping (but I think that lacks clarity).

You can now do this:

var data = {firstName:"John"};
var person = ko.mapping.fromJS(data, PersonViewModel.mapping);

This gets more fun when you have nested Observable Arrays of Objects

function PeopleViewModel(data) {
  var self = this;
  self.teachers = ko.mapping.fromJS([], PersonViewModel.mapping);
  self.students = ko.mapping.fromJS([], PersonViewModel.mapping);
  
  ko.mapping.fromJS(data, {}, self);
}
PeopleViewModel.mapping = {
  create: function(options){
    return new PeopleViewModel(options.data);
  },
  teachers: PersonViewModel.mapping,
  students: PersonViewModel.mapping
  // Reuse the mapping defined above!
};

var data = { 
  teachers: [
    { firstName: "John" },
    { firstName: "Marc" }
  ],
  students: {
    { firstName: "Bob"}
  }
};
var peopleVM = new PeopleViewModel(data);
var index = peopleVM.teachers.mappedIndexOf({firstName:"Marc"});

// index == 1

Summary

Some notes on structuring your ViewModel classes for readability, avoiding binding issues and Knockout Mapping gotchas.

You can try the example in this Plunker

Moving Everyone and Everything to AngularJS

We are late on the boat.  But I'm describing the journey for us, and we are truly on the boat.

 

So in mid 2014, SharePoint Gurus decided that we'll move everyone to JavaScript.  Traditionally, we are very much an InfoPath, Workflows and Configuration-over-Customization type of Consultancy.

My background is hardcore ASP.NET so I get all the fun stuff of writing Full Trust and Sandbox Solutions.  Custom Web Parts, Feature Receivers and Workflow Activities.  Later, as more work moves to client-side scripting, I would write Full Trust REST service end-points that provide lots of functionality to our InfoPath and Front-End Developers.

As InfoPath goes on a slowly (but surely) spiral of death, we look forward to where we should be investing our company's time and skills.  I was asked to consider and plan out the migration path for the company.

What we looked at:

JQuery

JQuery itself was a good starting point, and possibly the simplest JavaScript library one should start with.  Traditionally, it's used for DOM manipulation, but it's usefulness was extended with jQuery Ajax and jQuery Deferred/Promise.  We use JQuery and the various plugins for JQuery for a lot of things.  Additionally, JQuery works very well with SPServices on SP2007 customizations.

KnockoutJS

KnockoutJS is a DataBinding library.  JQuery is great for creating Read Only, One-Way DOM creation (say, you query an REST end point and want to create a bunch of elements in a table or list).  But JQuery is terrible at keeping track of the UI and the Original JavaScript object for you to post that data back.

KnockoutJS fixes that problem.  The good side is that it's intuitive.  The downside is that because JavaScript properties doesn't have onChanged() method, KnockoutJS creates observable property wrappers instead, and the syntax is not as pretty.

DurandalJS

Since KnockoutJS is only a databinding library, when you want to start looking at creating Single Page Applications, you need more stuff.  You need a Module Loader.  You need a Routing System.  And you need View Composition.

Durandal uses KnockoutJS for data binding, and adds RequireJS for module loading and JQuery for AJAX calls.

Although I loved DurandalJS, ultimately I could not recommend it to the whole company because the creator Rob Eisenberg (@eisenbergeffect) moved to AngularJS (and later to Aurelia.io).  I am keen personally on Aurelia, but it is not ready to introduce to the whole company in 2014.

ASP.NET MVC

I personally LOVE ASP.NET MVC.  But here's the problem - we have developers who are used to ASP.NET WebForms, so for them there are two jumps: first they have to learn ASP.NET MVC and then they still have to learn JavaScript.

JavaScript is required learning.

And ASP.NET MVC needs SharePoint Provider-Hosted apps - we can't, for example, use it with SharePoint 2010 or SharePoint 2007.

We have not turned our backs to ASP.NET MVC forever, it's just for the immediate focus, we are fully committed to client-side (Front-End) development technologies.  A few of our advanced guys will double back and pick up ASP.NET MVC later (or NodeJS).

AngularJS

All choices aside, we decided on pushing forward with AngularJS.  For many in the team, they have experience with JQuery but not DurandalJS nor KnockoutJS - so there's not a lot of wasted learning.

For me, I always compare AngularJS to DurandalJS and the biggest issue I have personally is that AngularJS is...  too magical.  UN-intuitively magical.  There are a lot of very custom syntax that you just have to know.  If you didn't know, you wouldn't know it's available.  But if you are looking at someone else's work, you can generally work backwards and understand Ah that's how it fits.  I wish it was more intuitive.

The biggest advantage for AngularJS is just how much community momentum there is for it.  There are a lot of people that understand it well, and lots of people building applications with it, and discussing it on the Internet - anywhere you look.

Additionally, lots of AngularJS and SharePoint / Office 365 training is available online.  Which means that we don't have to re-invent every wheel.

As a company and for our teams, it is a very safe bet.

There are a few related technology in the stack that we are using:

Bootstrap and Font-Awesome both give the front-end developer two things:  Rapid layout and font-glyphs without mucking around outside of the HTML templates and JavaScript code.

AngularJS + TypeScript

Since AngularJS announced that it will be built in TypeScript, there has been a resurgence of activity on the TypeScript front.

If your team has a lot of C# developers, TypeScript is worth your time to invest into.  It will make your team more productive, and as a team, you will write much better code.

 

What I did NOT look at:

All that said, there's also a lot that's unsaid.  I did not look into Backbone, EmberJS or Telerik KendoUI.  The main reason really is that I was personally very happy with DurandalJS, and if it was up to me personally, I would have chose Aurelia to go next.  But the evaluation we went through is to decide which framework to pursue for the entire company.

I always consider that we should never stop learning.  In the JavaScript space this is even more true.  Just like back in 2008 we decided (in my previous company) that we will pursue JQuery, in 2014 - we made a similar decision to pursue AngularJS.

 

An Army of Front-End Developers

So as a whole, our team is turning from InfoPath designers into Front-End Developers.  We have mastered JQuery and now diving into the fun that is AngularJS Directives.

 

Aim to have An Army of Full-Stack Developers in the nearby future

Moving towards a proper MEAN stack.  There are other components to tackle next:

  • M - MongoDB (a NoSQL document database suitable for storing XML and JSON)
  • E - Express (a lightweight web server, aka IIS)
  • A - AngularJS (front-end)
  • N - NodeJS (run JavaScript on the server, aka C# or .NET Framework)
  • Everything will have to run Provider-Hosted, most likely on Azure

For now - we are Front-End developers and happy with just the UI and storage of the form contents.

  • A - AngularJS
  • S - SharePoint Library (storage of XML and JSON - can be easily loaded via client-side scripting)

Summary

https://twitter.com/alwaysbetonjs

https://twitter.com/alwaysbetonjs


TypeScript Definition file for SPServices v0.1

 

Here's something I have wanted to write for a long time, both as Thank-You to Marc Anderson's work on SPServices, as well as for personal learning of the TypeScript language.

 

Adding TypeScript Definition reference:

image

 

$().sp (intellisense)

image

 

$().SPServices.defaults.cacheXML

image

 

$().SPServices.Version()

image

 

$().SPServices.SPCascadeDropdowns({ ... })

image

 

And one more.

$().SPServices({ ... })

image

 

Syntax Error Detection that TypeScript does so well. 

Except for the error message.

Repeat after me in a ROBOT way.  "String is missing apply from type Function".

What it means is I was expecting a function why did I find a string. 

And because Javascript can be surprisingly retarded, TypeScript tries to accommodate and wonders: perhaps this string is a function in disguise - so let me look for the apply method on this object.  NOPE.  It is not a function.   /s

 

image

 

 

Bonus.  TypeScript understands SPServices returns a JQueryXHR promise object.

image

 

Wait.  Where's all the other methods?

 

This is v0.1...

 

Download

https://static1.squarespace.com/static/5527bff2e4b0b430660b0d10/5527c30de4b030eeeef09715/5527c30ee4b030eeeef09d1c/1418221412217/jquery.SPServices.d.ts

Also, how do I get this into Marc's repo?