Step 4: Creating the Web Form Pages That Depend on the Main.Master File
Default-src.aspx
Create a Web Form using Master Page in the debug folder called
Default-src.aspx
.
This turns out to be a mercifully short file. This is one of the great advantages of the .NET approach to Master pages. There are only two ContentPlaceHolders in the master page. Add the links to your JavaScript files as follows to the Content element linked to the JsScripts ContentPlaceHolder:
01
02
03
04
05
06
07
08
09
10
| <%@ Page Title="Default src" Language="C#" MasterPageFile="~/Main.Master" AutoEventWireup="true" CodeBehind="Default-src.aspx.cs" Inherits="NetTutsMsBuildJs.debug.Default_src" %> < asp:Content ID = "Content1" ContentPlaceHolderID = "head" runat = "server" > </ asp:Content > < asp:Content ID = "Content2" ContentPlaceHolderID = "JsScripts" runat = "server" > < script src = "/js/jquery-1.8.2.min.js" ></ script > < script src = "/js/jquery-ui-1.9.2.custom.min.js" ></ script > < script src = "/debug-js/src/jquery.ui.menubar.js" ></ script > < script src = "/js/knockout-2.1.0.js" ></ script > < script src = "/debug-js/src/default-src.js" ></ script > </ asp:Content > |
Create a new JavaScript file called
default-src.js
in the debug-js\src folder
.
We enclose everything in a call to the usual jQuery
$
function that makes sure the page is fully loaded, before running anything.
1
2
3
| $( function () { }); |
As of jQuery 1.4, if the JSON file contains a syntax error, the request will usually fail silently. See:jQuery.getJSON().
We need three main pieces of functionality here:
- A call to the jQuery
getJSON
method to retrieve the JSON data for the menubar. If that succeeds, we create a Knockout view model and callko.applyBindings(viewModel)
to activate it. - A
renderMenu
function which will be called by the afterRender event of the MenuTemplate. This function calls themenubar
constructor to render the menubar. - A
select
function which is called when the user clicks a menubar item. This function retrieves the JSON data from the relevant content file and displays it on the page.
Notice that the select function needs to be able to retrieve the URL from the underlying JSON data. This is the trickiest part of marrying the jQuery menubar functionality with the Knockout template. jQuery allows you to add data to and retrieve data from an HTML element. To add data from within our Knockout template, we need to use a custom binding, which has access to the HTML element it is bound to. The binding I have created is called
addData
and is simply attached to ko.bindingHandlers
in theusual Knockout way with an init
method and an update
method.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| ko.bindingHandlers.addData = { init: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (value) { $.data(element, "url" , value); } }, update: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (value) { $.data(element, "url" , value); } } }; |
Perhaps the node template makes more sense now. The jQuery object passed as ui in the
select
handler represents the topmost li
element of each menubar item, so we add the custom binding to that list item element: data-bind="addData: $data.url"
. Now that each element has some data attached to it, we can retrieve it from theselect
handler with this syntax: ui.item.data("url")
, using the jQuery data
method.
The link element is more straightforward and just uses the standard
attr
and text
bindings:
1
2
3
| < a data-bind = "attr: {href: ('#' + $data.url)}" > < span data-bind = "text: $data.text" ></ span > </ a > |
Just note that I've prefixed the href with a hash symbol. That way when you click on the menubar item, you don't follow a link to another page. Instead, the
select
event is fired and the handler, sorry, handles it.
Here's the full select function using this approach to retrieve the data from the jQuery object representing the element rendered by Knockout:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
| function select(event, ui) { var url = "/json/" + ui.item.data( "url" ); $.getJSON(url, function (data) { viewModel.header(data.header); viewModel.text(data.text); }) .error( function (errorData) { viewModel.header( "Error" ); if (errorData.status === 404) { viewModel.text( "Could not find " + ui.item.text() + " at " + url); } else { viewModel.text( "There has been an error, probably a JSON syntax error. Check the JSON syntax in the file <code>" + url + "</code>" ); console.log(errorData); } }); } |
I added the extra error trap because jQuery now remains silent about JSON syntax errors. I don't want the user to be burdened with the details of JSON syntax errors, but I want to give some clue about what might have gone wrong.
Here's the Knockout view model defined in the function attached to the
getJSON()
method:
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
| $.getJSON( '/json/menubar-data.json' , function (data) { viewModel = { header: ko.observable(), text: ko.observable(), masters: ko.observableArray([ { name: "Master1" , root: data } ]), renderMenu: function () { $( "#menu" ).menubar({ autoExpand: true , menuIcon: true , buttons: true , select: select }); } }; ko.applyBindings(viewModel); viewModel.header( "Welcome" ); viewModel.text( "The English Resource Page" ); }) .error( function (errorData) { console.log({ "errorData" : errorData }); console.log(errorData.error()); }); |
Step 5: Run the Project in Debug Mode.
With run (the green arrow just under the menu of the IDE) in Debug mode.
Default-src.aspx
open in the IDE window, click
After the build process, the
Default-src.aspx
should appear in your browser's window. The IDE runs an Express version of the IIS web server in the background. In my case, the project uses port 54713 on localhost to run the page:
http://localhost:54713/debug/Default-src.aspx
We're now ready to work on the JavaScript build process.
Integrating the JavaScript Build Process Into MSBuild
This project will automate the two key steps we need to build a complex JavaScript project:
- Concatenate: Collect all the source files you need for a particular page and concatenate them together into one file. MSBuild doesn't have a built-in Concat task like Ant or NAnt so we'll have to roll our own based on this excellent blog How To: Concatenate files using MSBuild tasks.
- Minify: Minify our own source files and concatenate them with production release files, like the jQuery file, into one compressed file.
Step 1: Toggle Between the Project and Editing the Project Build File
The folder where you created your .NET project will include files that look like these:
No comments:
Post a Comment