Knockout.Unobtrusive Example
An example of using KnockoutJS without the default data-bind HTML5 markup. Uses the Knockout.Unobtrusive plugin
by bsatrom
HTML
<script src="http://knockoutjs.com/js/jquery.tmpl.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout/master/build/output/knockout-latest.js"></script>
<script src="https://raw.github.com/bsatrom/Knockout.Unobtrusive/master/js/knockout.unobtrusive.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Create</title>
<meta name="author" content="Brandon Satrom" />
<meta name="viewport" content="width=device-width; initial-scale=1.0" />
</head>
<body>
<div id="site-wrapper">
For a full writeup of this example, <a href="http://userinexperience.com/?p=633">click here</a>
<h2>Create a Speaker</h2>
<form data-bind="submit: addSpeaker">
<fieldset>
<legend>Speaker Info</legend>
Name: <input type="text" id="name" /> <br />
Bio: <input type="text" id="bio" /> <br />
Twitter Handle: <input id="twitterHandle" type="text" /> <br />
Photo Url: <input id="photoUrl" type="text" /> <br />
Presented Before: <input type="checkbox" name="presentedBefore" />
</fieldset>
<fieldset>
<legend>Favorite Languages</legend>
New item:
<input type="text" id="languageToAdd" />
<button type="submit" id="addLanguage">Add</button>
<p>Your choices:</p>
<select multiple="multiple" width="50" id="languages"> </select>
</fieldset>
<input type="submit" value="Create Speaker" />
<!-- Profile Preview -->
<article id="profilePreview">
<header>
<div><img id="image" /></div>
...
CSS
/*
Reset
------------------------------------------------------------------- */
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, textarea, input, select {margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline;}
table {border-collapse: collapse; border-spacing: 0;}
caption, th, td {text-align: left; font-weight: normal;}
table, td, th {vertical-align: middle;}
blockquote:before, blockquote:after, q:before, q:after {content: "";}
blockquote, q {quotes: "" "";}
a img {border: none;}
:focus {outline: 0;}
/*
General
------------------------------------------------------------------- */
html {
height: 100%;
padding-bottom: 1px; /* force scrollbars */
}
body {
background: #FFF;
color: #444;
font: normal 75% sans-serif;
line-height: 1.5;
}
/*
Typography
------------------------------------------------------------------- */
/* Headings */
h1,h2,h3,h4,h5,h6 {
color: #444;
font-weight: normal;
line-height: 1;
margin-bottom: 0.3em;
}
h4,h5,h6 {font-weight: bold;}
h1 {font-size: 2.6em;}
h2 {font-size: 2em;}
h3 {font-size: 1.5em;}
h4 {font-size: 1.25em;}
h5 {font-size: 1.1em;}
h6 {font-size: 1em;}
h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin: 0;}
/* Links */
a:focus,a:hover {color: #039;}
a {
color: #456;
text-decoration: none;
}
a:hover {text-decoration: underline;}
a.feed {
background: url('img/icon-feed.gif') no-repeat left center;
padding-left: 18px;
}
a.more {
color: #579;
font-weight: bold;
}
a.more:hover {color: #234;}
/* Text elements */
p {margin-bottom: 1em;}
abbr, acronym {border-bottom: 1px dotted #666;}
address {margin-bottom: 1.5em;}
blockquote {margin: 1.5em;}
del, blockquote {...
JavaScript
var viewModel = {
id: '',
name: ko.observable(''),
bio: ko.observable(''),
twitterHandle: ko.observable(''),
state: ko.observable(''),
photoUrl: ko.observable(''),
languages: ko.observableArray([]),
averageRating: ko.observable(''),
languageToAdd: ko.observable(''),
presentedBefore: ko.observable(true)
};
viewModel.twitterUrl = ko.dependentObservable(function() {
return "http://www.twitter.com/" + viewModel.twitterHandle();
});
viewModel.addLanguage = function() {
if (viewModel.languageToAdd() !== '') {
viewModel.languages.push(viewModel.languageToAdd());
viewModel.languageToAdd('');
}
};
viewModel.addTopic = function() {
if (viewModel.topicToAdd() !== '') {
viewModel.favoriteTopics.push(viewModel.topicToAdd());
viewModel.topicToAdd('');
}
};
viewModel.addSpeaker = function() {
$.ajax({
url: "/speakers/create/",
type: 'post',
data: ko.toJSON(this),
contentType: 'application/json',
success: function(result) {
alert(result);
}
});
};
var bindings = {
value: [ 'languageToAdd', 'name', 'bio', 'twitterHandle', 'state', 'photoUrl' ],
text: [ { displayName: 'name' }, { displayState: 'state' }, { displayBio: 'bio' }, { displayTwitterHandle: 'twitterHandle' }, { languageList: 'languages' }, { displayPresentedBefore: 'presentedBefore' } ],
options: [ 'languages', 'favoriteTopics' ],
checked: [ 'presentedBefore' ],
custom: {
languageToAdd: 'valueUpdate: "afterkeydown"',
addLanguage: 'enable: languageToAdd().length > 0, click: addLanguage',
photo: 'attr: {src: photoUrl, alt: name}',
twitterUrl: 'attr: {href: twitterUrl}'
}
};
ko.unobtrusive.createBindings(bindings);
ko.applyBindings(viewModel);
$('#profilePreview').hide();
$('input[type=text]').blur(function () {
$('#profilePreview').show();
});
$('#profilePreview').draggable();