In our previous post Dynamic UI Using Observables with MVVM Using Knockout.js, you learned how you get started with Knockout.js and how you can detect and respond to changes on one object using observables.
Knockout provides ways to bind the data you specify in the data-bind attribute to the element. You can apply bindings to the text and appearance, use them in the logic you use to display items, and working with form fields.
In addition, you can create your own custom bindings.
Let’s see how.
Controlling Text, Appearance Using Bindings
You can bind the the firstName property in the viewmodel to the text in the strong element.
<strong data-bind="text: firstName">
The text
binding causes the associated DOM element to display the text value of your parameter.
You typically use the text binding with elements like <span>
or <em>
that traditionally display text, but technically you can use it with any element.
Other bindings give you access to the element.
- visible. The
visible binding
causes the associated DOM element to become hidden or visible according to the value you pass to the binding. - html. The
html
binding causes the associated DOM element to display the HTML specified by your parameter. Typically this is useful when values in your view model are actually strings of HTML markup that you want to render. - css. The
css
binding adds or removes one or more named CSS classes to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative. - style. The
style
binding adds or removes one or more style values to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative, or to set the width of a bar to match a numerical value that changes. - attr. The
attr
binding provides a generic way to set the value of any attribute for the associated DOM element. This is useful, for example, when you need to set thetitle
attribute of an element, thesrc
of animg
tag, or thehref
of a link based on values in your view model, with the attribute value being updated automatically whenever the corresponding model property changes.
Show Hide Sample
Here’s a sample showing how each visible can be used with click, disable, enable bindings:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Show Hide</title> | |
</head> | |
<body> | |
<div data-bind="visible: onoff">Show Me</div> | |
<button data-bind="click: showMe, disable: onoff">Show Text</button> | |
<button data-bind="click: hideMe, enable: onoff">Hide Text</button> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script> | |
var viewModel = function () { | |
this.onoff = ko.observable(true); // onoff is intially set to true | |
hideMe = function() { | |
this.onoff(false); | |
}; | |
showMe = function () { | |
this.onoff(true); | |
}; | |
} | |
ko.applyBindings(new viewModel()); | |
</script> | |
</body> | |
</html> |
CSS and HTML Sample
The following Knockout.js sample shows how you can use html, attr, and css bindings.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title></title> | |
<style> | |
.redItem { | |
color: red; | |
} | |
.greenItem { | |
color: green; | |
} | |
</style> | |
</head> | |
<body> | |
<div data-bind="html: someHtml"></div> | |
<div>And a link <a data-bind="attr: { href: myHref }">#</a></div> | |
<div data-bind="css: profitStatus, text: currentProfit"></div> | |
<button data-bind="click: reviseProfit">Revise Profit</button> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script> | |
var viewModel = function () { | |
this.someHtml = "Here is a <strong>bold</strong> text."; | |
this.myHref = "#"; | |
this.currentProfit = ko.observable(150000); | |
this.profitStatus = ko.computed(function () { | |
return this.currentProfit() < 0 ? "redItem" : "greenItem"; | |
}, this); | |
this.reviseProfit = function () { | |
this.currentProfit(–50); | |
}; | |
}; | |
ko.applyBindings(new viewModel()); | |
</script> | |
</body> | |
</html> |
Control Flow Bindings
There are four bindings that help you with control flow. The foreach, if, ifnot, and with bindings provide ways for you to control what is displayed and how the data are displayed.
foreach Control Flow Binding
foreach.
The foreach
binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables.
The foreach binding is covered in the next post, Dynamic UI With MVVM Using ObservableArray in Knockout.js, that is tied to observableArrays.
if and notif Control Flow Bindings
The if
binding causes a section of markup to appear in your document (and to have its data-bind
attributes applied), only if a specified expression evaluates to true
(or a true
-ish value such as a non-null
object or nonempty string).
It is a lot like the visible binding in functionality, except that with visible the markup remains in the DOM and a CSS style applied to hide the element. The if binding adds and removes the markup in your DOM.
The ifnot
binding is exactly the same as the if
binding, except that it inverts the result of whatever expression you pass to it.
Here’s an example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Control Flow Bindings</title> | |
</head> | |
<body> | |
<label> | |
<input type="checkbox" data-bind="checked: displayMessage" /> | |
Display message</label> | |
<div data-bind="if: displayMessage">There's something important here.</div> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script> | |
ko.applyBindings({ | |
displayMessage: ko.observable(false) | |
}); | |
</script> | |
</body> | |
</html> |
with Control Flow Binding
The with
binding creates a new binding context, so that descendant elements are bound in the context of a specified object.
The following example, displays a product, along with related data that is nested:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title></title> | |
</head> | |
<body> | |
<h1 data-bind="text: product"></h1> | |
<p data-bind="with: warehouse"> | |
Warehouse Location: <span data-bind="text: location"></span>, | |
Quantity Available: <span data-bind="text: quanAvail"></span> | |
</p> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script type="text/javascript"> | |
ko.applyBindings({ | |
product: "Widget", | |
warehouse: { | |
location: "3D1", | |
quanAvail: 6 | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Binding to Control Types
The following is an example of binding view model properties to a range of HTML control types. This is from the Knockout Control types example in the Knockoutjs documentation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Binding HTML Controls</title> | |
</head> | |
<body> | |
<div class="readout"> | |
<h3>What's in the model?</h3> | |
<table> | |
<tr> | |
<td class="label">Text value:</td> | |
<td data-bind="text: stringValue"></td> | |
</tr> | |
<tr> | |
<td class="label">Password:</td> | |
<td data-bind="text: passwordValue"></td> | |
</tr> | |
<tr> | |
<td class="label">Bool value:</td> | |
<td data-bind='text: booleanValue() ? "True" : "False"'></td> | |
</tr> | |
<tr> | |
<td class="label">Selected option:</td> | |
<td data-bind="text: selectedOptionValue"></td> | |
</tr> | |
<tr> | |
<td class="label">Multi-selected options:</td> | |
<td data-bind="text: multipleSelectedOptionValues"></td> | |
</tr> | |
<tr> | |
<td class="label">Radio button selection:</td> | |
<td data-bind="text: radioSelectedOptionValue"></td> | |
</tr> | |
</table> | |
</div> | |
<h3>HTML controls</h3> | |
<table> | |
<tr> | |
<td class="label">Text value (updates on change):</td> | |
<td><input data-bind="value: stringValue" /></td> | |
</tr> | |
<tr> | |
<td class="label">Text value (updates on keystroke):</td> | |
<td><input data-bind='value: stringValue, valueUpdate: "afterkeydown"' /> </td> | |
</tr> | |
<tr> | |
<td class="label">Text value (multi-line):</td> | |
<td><textarea data-bind="value: stringValue"> </textarea></td> | |
</tr> | |
<tr> | |
<td class="label">Password:</td> | |
<td><input type="password" data-bind="value: passwordValue" /></td> | |
</tr> | |
<tr> | |
<td class="label">Checkbox:</td> | |
<td><input type="checkbox" data-bind="checked: booleanValue" /></td> | |
</tr> | |
<tr> | |
<td class="label">Drop-down list:</td> | |
<td><select data-bind="options: optionValues, value: selectedOptionValue"> </select></td> | |
</tr> | |
<tr> | |
<td class="label">Multi-select drop-down list:</td> | |
<td><select multiple="multiple" | |
data-bind="options: optionValues, selectedOptions: multipleSelectedOptionValues"> | |
</select></td> | |
</tr> | |
<tr> | |
<td class="label">Radio buttons:</td> | |
<td> | |
<label><input type="radio" value="Alpha" | |
data-bind="checked: radioSelectedOptionValue" />Alpha</label> | |
<label><input type="radio" value="Beta" | |
data-bind="checked: radioSelectedOptionValue" />Beta</label> | |
<label><input type="radio" value="Gamma" | |
data-bind="checked: radioSelectedOptionValue" />Gamma</label> | |
</td> | |
</tr> | |
</table> | |
<script type="text/javascript" src="Scripts/knockout-2.2.1.js"></script> | |
<script> | |
var viewModel = { | |
stringValue: ko.observable("Hello"), | |
passwordValue: ko.observable("mypass"), | |
booleanValue: ko.observable(true), | |
optionValues: ["Alpha", "Beta", "Gamma"], | |
selectedOptionValue: ko.observable("Gamma"), | |
multipleSelectedOptionValues: ko.observable(["Alpha"]), | |
radioSelectedOptionValue: ko.observable("Beta") | |
}; | |
ko.applyBindings(viewModel); | |
</script> | |
</body> | |
</html> |
Interesting parts to call out.
valueUpdate: “afterkeydown” provides the update to the viewmodel and to the observable elements after each key is pressed.
selectedOptions: multipleSelectedOptionValues provides the selected values.
and value attribute, checked bindings provide the value for the radio buttons.
Custom Bindings
You’re not limited to using the built-in bindings like click
, value
, and so on — you can create your own ones. This is how to control how observables interact with DOM elements, and gives you a lot of flexibility to encapsulate sophisticated behaviors in an easy-to-reuse way.
For example, you can create interactive components like grids, tabsets, and so on, in the form of custom bindings.
See Creating custom bindings in the Knockout documentation.