Archived Content
If you’ve ever used angular.js, you might have come across the transclude feature. From the angular.js docs:
Transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
This allows you to create directives that wrap other elements. e.g., if you have a small tooltip component with a bunch of boilerplate markup, you can abstract that away into a directive.
That is a lot of boilerplate to include if you want a bunch of tooltips on a page. With angular, you can create a custom element that will abstract this away. The problem is, you want your --SOME DYNAMIC CONTENT--
to bind against the outer scope, not the isolated scope of your directive. This is where angular’s transclude comes in. It will bind the dynamic content against the scope where the directive is being used and pass that result to the directive allowing the directive to use it but still keep its own isolated scope.
After you create your awesome little directive, you can then do something like this:
If this doesn’t make sense, I encourage you to read the newly updated (and much better than it used to be) angular.js docs on this.
Transclude in Knockout.js
Anyway, in knockout.js, which is the library we are using on one of my projects, there is no such thing as transclude. You can still abstract away components like this, but you would do it with a binding handler.
Not as nice of a syntax, but it still gets us there (see below for why we need to pass unique-name
). The problem is that the --SOME DYNAMIC CONTENT--
, if you just include it in your template, will not bind against the original model (which is what we want). The trick is to treat the inner html of our element as another template and bind it manually in our binding handler to the original model.
The key parts of this binding handler are:
- Making use of the lesser known fourth parameter of the update function - the original view model being used where this handler is being called from
- Using the inner HTML of our element as a new template and binding that template with the original view model
This could probably be abstracted into a separate component that provides this service, but I think it works better as just a reference example. In the end, I think it is an easy technique to get some really nice functionality.