In UML 2.0, "component" is used to describe a more abstract idea: autonomous, encapsulated units; "artifact" is used to describe what I'm showing in this diagram: files and libraries. It's an ideal diagram to show how the various files depend on each other. For instance, all the web form pages depend on the Main master page. The
js.build
file won't work if the compressor jar file is not present. The project file and thejs.build
file are, annoyingly, mutually dependent. If the js.build
file is not present, the project will not load; js.build
can't run alone, the tasks defined there are triggered by the AfterBuild event in the overall project build.
For this tutorial, I want to display a horizontal menubar using the menubar branch of jQuery UI. To do that, I have a JSON file with the hierarchical data for the menu and a Knockout.js template looping through this data to render the
HTML markup needed by jQuery menubar. I've added a callback function
HTML markup needed by jQuery menubar. I've added a callback function
renderMenu
which is fired by the afterRender
event in the Knockout template. renderMenu
then simply makes a call to menubar
to finally render the menubar with all the lovely jQuery UI shiny features.Step 1: The Production Release Files
CSS
Download the full bundle from jQuery UI including a theme of your choice. After unzipping your download, drill down to the folder called
css
where you'll find a folder with the name of your chosen theme. In my case, I've chosen smoothness. Open that folder and you should see the files you need:
Copy the whole theme folder (smoothness) and paste it into your
css
folder in the project. Come back to Visual Studio, click the refresh icon at the top of the Solution Explorer and the smoothness folder should appear in the css
folder. You should include the folder in the project as well.
In addition to jQuery UI and a specific theme, you also need the small CSS file specifically for the menubar. After downloading the menubar project from github, drill down to the
jquery.ui.menubar.css
file following this path: \jquery-ui-menubar\themes\base\jquery.ui.menubar.css
. Copy that to the css
folder of your project.JavaScript
Download up-to-date versions of production releases of jQuery, jQuery UI and Knockout. I'm using 1.8.2 for jQuery, 1.9.2 for jQuery UI and 2.1.0 for Knockout. Copy them to the
js
folder in your project.
You'll also need the latest, uncompressed release of
jquery.ui.menubar.js
, downloaded from the Menubar branch of the jQuery UI project. Copy that to the debug-js\src
folder in your project.The Main Master Page
We're creating several versions of the same page to help debug and test our JavaScript. The master page can of course help to prevent duplication of code. Call this master page
Main.Master
.
Leave the title element blank (we'll define the title for each page that uses this master) and link to all the stylesheets we need for jQuery UI and the menubar:
1
2
3
4
| < title ></ title > < link rel = "stylesheet" type = "text/css" href = "/css/smoothness/jquery-ui-1.9.2.custom.css" > < link rel = "stylesheet" type = "text/css" href = "/css/smoothness/jquery-ui-1.9.2.custom.min.css" > < link rel = "stylesheet" type = "text/css" href = "/css/jquery.ui.menubar.css" > |
Add a
ContentPlaceHolder
just before the end of the body where each page will link to the relevant JavaScript files
1
| < asp:ContentPlaceHolder ID = "JsScripts" runat = "server" /> |
Step 2: The JSON Definition of the Data Needed for the Menubar
Here is the JSON object defining a menubar that we might use for an English Instructors' website. Create a JSON file called
menubar-data.json
in the json
folder and populate it with the following JSON.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| { "nodes" :[{ "text" : "For Students" , "nodes" : [ { "text" : "Listening Practice" , "url" : "listening-practice.json" }, { "text" : "Grammar" , "url" : "grammar.json" , "nodes" : [ { "text" : "Verb Forms" , "url" : "verb-forms.json" , "nodes" : [ { "text" : "Verb Tense and Aspect" , "url" : "verb-tense-and-aspect.json" }, { "text" : "Modal Auxiliary Verbs" , "url" : "modal-auxiliary-verbs.json" } ] }, { "text" : "Verb Patterns" , "url" : "verb-patterns.json" }, { "text" : "Noun phrases" , "url" : "noun-phrases.json" }, { "text" : "Complex sentences" , "url" : "complex-sentences.json" } ] } ] }, { "text" : "For Teachers" , "nodes" : [ { "text" : "Teaching Materials" , "url" : "teaching-materials.json" }, { "text" : "Tests and evaluation grids" , "url" : "tests-and-evaluation.json" }, { "text" : "Media" , "url" : "media.json" } ] } ] } |
Top-level nodes have no URL property defined, so when clicked, they will just display sub-menu items. The sub-menus contain nodes with the URL property defined. When you click one of these nodes, the system will retrieve the JSON data from the file at that URL.
Each JSON file linked to, in the menubar, contains some content in a simple structure defining a header and some text:
1
2
3
4
| { "header" : "Grammar" , "text" : "A series of exercises helping you to improve your grammar." } |
Step 3: The Knockout Template for the Menubar
We define this in
Main.Master
. There is no obvious way of minifying or improving on it for deployment so I want to re-use it with every version of the pages that link to the master page.
I wanted to have just one Knockout template to render the HTML markup (a set of nested
ul
elements) for the menubar, but not surprisingly the afterRender
event associated with the foreach
binding fires with every loop, not at the end of the whole rendering process. So, I needed to create an observableArray
with only one ul
element, bind that to a Menu template which renders the outermost ul
element, and nest the menubar template inside it. I can then handle that single foreach
event with my function renderMenu
, which calls the jQuery menubar constructor and renders the menubar in all its glory. I got a lot of help on this from this thread: nested-templates-with-knockoutjs-and-mvc-3-0.
Here is the menu template:
1
2
3
| < script type = "text/html" id = "MenuTemplate" > < ul class = "ui-widget-header" id = "menu" data-bind = "template: { name: 'MenuNodeTemplate', foreach: $data.root.nodes}" ></ ul > </ script > |
And here's the node template for each node of the menubar:
1
2
3
4
5
6
7
8
| < script id = "MenuNodeTemplate" type = "text/html" > < li data-bind = "addData: $data.url" > < a data-bind = "attr: {href: ('#' + $data.url)}" >< span data-bind = "text: $data.text" ></ span ></ a > <!-- ko if: $data.nodes --> < ul data-bind = "template: { name: 'MenuNodeTemplate', foreach: $data.nodes}" ></ ul > <!-- /ko --> </ li > </ script > |
You then need a
div
element which you bind to MenuTemplate:
1
| < div data-bind = "template: {name: 'MenuTemplate' , foreach: masters, afterRender: renderMenu}" ></ div > |
Notice that the node template uses containerless control flow syntax, which is based on comment tags. There are a few things going on here, so let me explain
In the fully rendered jQuery menubar, I want to attach a handler to the
select
event. The handler has the signature event, ui
. When you click a menubar item, the handler is passed the event object and a jQuery object representing the item. To get the text from the ui
object, we can call the text method ( ui.item.text()
). But how do we get the url
property from the underlying JSON? That is a little bit trickier and I explain it later when we look at the select function triggered by the click event on each sub-menu item and the custom binding addData
attached to the li
element in the Knockout template.
Finally you just need a
div
element where we can display the content retrieved from the JSON data files:
1
2
3
4
| < div id = "show-result" class = "ui-widget" > < h1 data-bind = "text: header" class = "ui-widget-header ui-corner-all" ></ h1 > < div data-bind = "html: text" class = "ui-widget-content ui-corner-all" ></ div > </ div >
|
No comments:
Post a Comment