Last active
December 22, 2017 00:52
-
-
Save jacobslusser/2d9210606a8ab64f3a4df7747ee40404 to your computer and use it in GitHub Desktop.
A RequireJS plugin for loading and registering Knockout components/templates
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
// The component is required using the 'component!' plugin we created. This will load | |
// 'page1.html' and 'page1.js' and return 'page1.js' to us. | |
define(['knockout', 'component!page1'], function (ko, Page1) { | |
'use strict'; | |
function App() { | |
var args = { name: 'Jacob' }; | |
// Example of using the component binding with the automatically registered component | |
this.sampleComponent = ko.observable({ | |
name: 'page1', | |
params: args | |
}); | |
// Templates can also be used and are probably preferable because we have easy access | |
// to the view model if we want to programmatically call anything on the view model. | |
// i.e. sampleTemplate().data.yourFunction() | |
this.sampleTemplate = ko.observable({ | |
name: 'page1', | |
data: new Page1(args) | |
}); | |
this.init = function init() { | |
ko.applyBindings(this); | |
}; | |
} | |
return new App(); | |
}); |
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 lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title>Knockout RequireJS Plugin</title> | |
</head> | |
<body> | |
<div data-bind="component: sampleComponent"></div> | |
<div data-bind="template: sampleTemplate"></div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.2/require.js" data-main="main.js" async></script> | |
</body> | |
</html> |
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
(function () { | |
'use strict'; | |
// A simple RequireJS plugin that will load a view and view model based on naming convention. | |
// Once loaded the view is added to the DOM so it can be used with KO templates and the view | |
// and view model are registered as a KO component. | |
define('component', ['jquery', 'knockout'], function ($, ko) { | |
return { | |
load: function (name, parentRequire, onload, config) { | |
parentRequire([name, 'text!' + name + '.html'], function (viewModel, view) { | |
// Add the view to the DOM | |
var template = '<script type="text/template" id="' + name + '">' + view + '</script>'; | |
$('body').append(template); | |
// Register as a KO component | |
ko.components.register(name, { | |
viewModel: viewModel, | |
template: { element: name }, | |
synchronous: true | |
}); | |
onload(viewModel); | |
}, function (err) { | |
onload.error(err); | |
}); | |
} | |
}; | |
}); | |
// Standard RequireJS stuff... | |
requirejs.config({ | |
paths: { | |
'jquery': 'https://code.jquery.com/jquery-1.12.4', | |
'knockout': 'https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-debug', | |
'text': 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text' | |
} | |
}); | |
// Start the application | |
require(['app'], function (app) { | |
app.init(); | |
}); | |
}()); |
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
<div> | |
<h1>Welcome: <span data-bind="text: userName"></span></h1> | |
</div> |
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
define(['knockout'], function (ko) { | |
'use strict'; | |
function Page1(params) { | |
this.userName = ko.observable(params.name); | |
} | |
return Page1; | |
}); |
NOTE: I've offer additional explanations and how this approach solves several issues in the following knockout/knockout threads:
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The
'component'
plugin in themain.js
file is the meat and potatoes, whileapp.js
is an example of how to use it, and the rest of the files are just here for completeness.Using a plugin to load/register components/templates has the following benefits over the recommend AMD module pattern in the Knockout documentation:
'page1.js'
which has a dependency on'text!page1.html'
, simply require'component!page1.js'
. It's less to write and has better performance because both the view and view model will be requested in parallel rather than in serial.synchronous = true
flag to be used and avoid most of the problems of not knowing when a component has finished updating the DOM. See knockout/knockout#1475, specifically the workaround suggested by @jods4 (here).'component!page1.js'
is thePage1
constructor function which you cannew
-up and use with thetemplate
binding. See an example of that inapp.js
which makes it dead simple to call functions on your view model, i.e.sampleTemplate().data.yourFunction()
.NOTE: I've used jQuery when adding the view to the DOM to simplify the example but that is not strictly necessary if you would prefer to use just vanilla JS.