-
Notifications
You must be signed in to change notification settings - Fork 6
Templating Syntax
Templates allow you to define your view markup, data bindings, and event handlers declaratively. A declarative approach means that by default, they are written in an easily parseable, easily translatable format. As technology improves, the templates can be translated into new formats in the future if necessary.
Views should each be defined in their own directory under your project's src directory. So to make a new view, make a new directory under src that is TitleCased, indicating the class name of your view. For example, ToggleButton.
A view file is simply an html file that gets parsed and translated into a TypeScript class. The root element must have a js-type attribute, which defines the classname.
src/ToggleButton/ToggleButton.html:
<div js-type="ToggleButton"></div>
State data is sent to the view as a json object containing name/value pairs that will be mixed into the view's viewModel. For example:
var toggleButton = new ToggleButton();
toggleButton.setData({ isToggled: true });
console.alert(toggleButton.viewModel.isToggled); // alerts 'true'
In your template, you can use the js-bind attribute to indicate a binding of the viewModel state to the effect it has on the view. In the following example, I bind the text value of the button to the viewModel property "name", and the className "toggled" to the viewModel property "isToggled".
<button
js-type="ToggleButton"
js-bind="text:name, className.toggled:isToggled">
</button>
A js-bind statement by default binds to the view's viewModel scope.
Here is an example which binds an anchor's text to the "name" property, the "href" attribute to the "url" property, and toggles the "isEnabled" class name if the "isEnabled" property is truthy:
<a js-bind="text:name, alt:altText, href:url, className.isEnabled:isEnabled"/>
The format of the js-bind syntax is:
[destinationType][.destinationSubType]:[sourcePath]<,[...]>
Where destinationType can be:
-
text - binds the text value of the element to the source value.
-
html - binds the html value of the element to the source value. Careful, this is not encoded.
-
attr - binds an attribute (provided as the subType) to the source value. This is the default binding type if no destinationType is provided. (e.g. binding "href:foo" will bind the href attribute to foo.)
-
className - toggles a given class name (provided as the subType) based on the source value's truthiness. (e.g. binding "className.foo:bar" will enable the class "foo" on the element if the source "bar" resolves as truthy.
The sourcePath defaults to the viewModel scope.
You can use a dot notation to drill into a specific value within the viewModel:
<div js-bind="text:item.name"></div>
If you want to bind to child view scopes, you can use the $ syntax, which indicates to "start at the view to resolve the scope" rather than the viewModel.
For example, you can bind to a child view's "isToggled" viewModel state:
<div js-type="MyView" js-bind="className.isHighlighted:$toggleButton.isToggled">
<toggle-button js-name="toggleButton" />
</div>
Often, a source needs to be massaged to make it "fit" into the experience. For example, you may want logic that multiplies a value by 3, or upper cases a value, or makes a date string friendly. For these scenarios you can bind directly to functions and you can pass the resolves values of existing properties into them.
For example, in my viewModel, I may have a toUpper helper that I want to wrap some of my binding sources within:
src/Foo/FooModel.ts:
class FooModel extends ViewModel {
name = "david";
toUpper(val) {
return val ? val.toUpperCase(): '';
}
}
src/Foo/Foo.html:
<div js-type="Foo" js-bind="text:toUpper(name)"></div>
Renders this markup:
<div>David</div>
General DOM events can be mapped to functions either defined on the viewModel (default) or the actual view implementation when you want to hide view logic from the viewModel implementation.
Here's an example of listening to a click event on a button, which binds to the _onClick handler:
<button js-event="click:_onClick">click me</button>
When the view is "activated", the event will automatically be observed and will call the function.
TBD
TBD