In my last post I took a good look at the way custom property values are passed around in the knockout template of an SPFX web part project. I found it really confusing and it took me a little while to understand what was going on – so I thought it might be useful to record my findings: https://codeforcloud.com/2016/10/13/sharepoint-framework-with-knockoutjs-making-sense-of-custom-properties/ .
In short, we’re going to:
- Create a new EventBus class that holds all our properties we want to pass around
- Push our property changes into the eventbus
- Subscribe / read property updates from the eventbus in our ViewModels / views.
It’s simpler to see it in action.
- Create the EventBus class. Here we use the singleton pattern to create a new class – this is important as we only ever want one event bus running. Add all your own properties in here (you’ll see “myProp” I’m using for demo)
- Import your EventBus into your WebPart class, and push values into it. Have a look through the code and we’ll point out the important lines below:
Take note of:
- Line 10: private eventBus = EventBus.getInstance(); . This gets the instance of the event bus we want – remember we’re using a singleton.
- Line 36: this.eventBus.myProp(this.properties.myProp || ”); . The render() method is called when properties in the web part are changed by the user – so here we push the new values (given to us in this.properties) into the event bus.
- Import your EventBus into your ViewModel / other modules, and use the values. Again have a look at my ViewModel code and we’ll pull out the highlights below:
Take note of:
- Line 9: private eventBus = EventBus.getInstance(); . Again, we get the instance of the EventBus we’re using elsewhere.
- Line 14: self.eventBus.myProp.subscribe… Using a knockout subscription we can – optionally – subscribe to changes in the value. This means we don’t actually need to recreate the property variables in our ViewModel (or the webpart class, for that matter).
- Reference the properties in a View. To complete the circle, we can directly reference our property values in our view markup, should you wish. As stated above, this means we don’t need to have multiple “var myProp” declarations in our project – we only declare the property values in the EventBus, and just reference them elsewhere.
When we use the above approach, it means we don’t actually need the following (I’d like to get some word from MS on the below really…)
- IMyWebPartProps.ts interface (so instead your web part declaration might look like: export default class MyCoolWebPart extends BaseClientSideWebPart<any>…)
- The private _myProp variables in the web part class
- The _shouter object in the web part class
- Any subscriptions / .notifySubscribers calls in the web part class
- The IBindingContext class in your ViewModel, and the instantiation of it in your WebPart. That can all go. Instead, you can just new up your ViewModel in the WebPart class and bind with that instead:
var vm = new MyCoolViewModel();
The above approach has a number of benefits, in my view:
- Separation of concerns – the classes are much less tightly coupled
- Less plumbing code
- Less subscriptions and property values bouncing around
- Simpler debugging when things go wrong.
Any comments or suggestions on the above, i’d love to hear them. Otherwise – I hope it’s useful 🙂