RadioButton web control with Knockout.js and MVVM

Here is an example of a RadioButton control using Knockout and MVVM. Hope this helps anyone looking.

<!-- Step 1 - Create the HTML File or the View -->
<!DOCTYPE html>
<html>
<head>
    <!-- Step 2 - Include jquery and knockout -->
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.pack.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
     
    <!-- Step 3 - Add script to run when page loads -->
    <script type="text/javascript" >
        $(document).ready(function(){
 
            <!-- Step 4 - Add a RadioButtonModel -->
            var RadioButtonModel = function(parent, inText, inValue, inGroupName, canClickMethod) {
                    // Private
                    var _self = this;
                    var _canClickMethod = (canClickMethod) ? canClickMethod : function() { return true; };
                    var _parent = parent;

                    // Public
                    this.text = ko.observable(inText);
                    this.value = ko.observable(inValue);
                    this.group = ko.observable(inGroupName);
                    this.class = ko.observable();
                    this.isSelected = ko.computed(function() {
                        return (_parent && _parent.selectedValue) ? _parent.selectedValue() == _self.value() : false;
                    }, _self);
                    _self.canClick = ko.computed(function() { return _canClickMethod(); });
                };
                
                
            <!-- Step 5 - Add a RadioButtonViewModel -->
            var RadioButtonViewModel = function (rbLabels, rbValues, group, defaultValue) {
                // Private
                var _self = this;
                var _rbLables = rbLabels;
                var _rbValues = rbValues;
                var _group = group;
                var _defaultValue = defaultValue;                

                // Public
                _self.selectedValue = ko.observable(_defaultValue);
                
                _self.list = ko.observableArray();
                for (var i = 0; i < _rbLables.length; i++) {
                    _self.list.push(new RadioButtonModel(_self, _rbLables[i], _rbValues[i], _group));
                }

                _self.selectedIndex = ko.computed(function () {
                    var i = 0;
                    var foundIndex = -1;
                    ko.utils.arrayForEach(_self.list(), function (item) {
                        if (_self.selectedValue() == item.value()) {
                            foundIndex = i;
                        }
                        i++;
                    });
                    return foundIndex;
                }, _self);

                _self.selectedText = ko.computed(function () {
                    return (_self.list()[_self.selectedIndex()]) ? _self.list()[_self.selectedIndex()].text() : "";
                }, _self);
                
                _self.clear = function(value){
                    _self.selectedValue(value ? value : "");
                }
                
            }
 
            <!-- Step 6 - Create ViewModel for whatever you need -->
            function SurveyAnswerViewModel() {
                var self = this;
                 
                <!-- Step 7 - Create an observable list instance -->
                self.rbGroup1 = new RadioButtonViewModel(new Array("Good", "Average", "Poor"), new Array(10,5,1), "Group1", 10);                           
                 
                <!-- Step 8 - Create a computed value to require a selection before submitting -->
                self.canClick = ko.computed( function() {
                    return self.rbGroup1.selectedValue() != "";
                }, self);
                
                <!-- Step 9 - Make some button methods for this example -->
                self.submitClick = function(){
                    // Do something here
                }
                
                self.resetClick = function(){
                    self.rbGroup1.clear();
                }                 
            }
            
            <!-- Step 10 - Create a computed value to require a selection before submitting -->   
            ko.applyBindings(new SurveyAnswerViewModel());
        });
    </script>
</head>
<body>
    <!-- Step 11 - Create a div containing the html for one radio button and bind to foreach: list -->
    <div data-bind="foreach: rbGroup1.list">
        <div>
            <input type="radio" data-bind="attr: {name: group}, value: value, checked: $root.rbGroup1.selectedValue, checkedValue: value" />
            <span data-bind="text: $index() + '. ' + text()"></span>
            <span data-bind="text: 'Is selected: ' + isSelected()"></span>
        </div>
    </div>
    <br />
    
    <!-- Step 12 - Add html elements to see other properties -->
    <p data-bind="text: rbGroup1.selectedValue"></p>
    <p data-bind="text: rbGroup1.selectedText"></p>
     
    <!-- Step 13 - Add a button and bind its to methods -->
    <button type="submit" id="btnSubmit" data-bind="enable: canClick, click: submitClick">Submit</button>
    <button type="reset" id="btnReset" data-bind="enable: canClick, click: resetClick">Reset</button>
 
</body>
</html>

Using slideToggle with Knockout’s MVVM

I recommend you use the third option: Option 3 – Boolean binding using a custom binding handler called slideToggle

See it live here: http://plnkr.co/edit/1YGchAyjkNSjYzmXFfK2

<!-- Step 1a - Create the HTML File or the View -->
<!DOCTYPE html>
<html>
<head>
    <!-- Step 2 - Include jquery and knockout -->
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.pack.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
     
    <!-- Step 3 - Add script to run when page loads -->
    <script type="text/javascript">
        $(document).ready(function(){
         
            <!-- Step 4 - Create a ViewModel -->           
            function viewModel() {
                _self = this;
                _self.showHideChild = function(viewModel, event) {
                    $(event.currentTarget).children('div').slideToggle()
                };
                _self.showHideNextSibling = function(viewModel, event) {
                    var siblings = $(event.currentTarget).siblings('div');
                    for (var i=0;i<siblings.length;i++) {
                        if (siblings[i].previousElementSibling == event.currentTarget ) {
                            $(siblings[i]).slideToggle();
                        }
                    }                 
                };
                _self.isVisible = ko.observable(false);
                _self.showHide = function() {
                    _self.isVisible(!_self.isVisible());
                }
            };
           
            ko.bindingHandlers.slideToggle = {
                init: function (element, valueAccessor) {
                    var value = valueAccessor();
                    $(element).toggle(ko.utils.unwrapObservable(value));
                },
                update: function (element, valueAccessor) {
                    var value = valueAccessor();
                    var isVisible = element.offsetWidth > 0 && element.offsetHeight > 0;
                    if (ko.utils.unwrapObservable(value) != isVisible) {
                        $(element).slideToggle();
                    }
                }
            };
           
            <!-- Step 5 -  Activates knockout.js bindings -->
          ko.applyBindings(new viewModel());
        });
    </script>
</head>
<!-- Step 4 - Create a HTML Elements with bindings -->
<body style="">
    <div>
        Option 1 - Child div
        <div id="child1" data-bind="click: showHideChild" style="border:2px solid;">
        Click me
            <div id="child2" style="display: none;">
              Now you see me!
            </div>
        </div>
        
        Option 2 - Siblings div
        <div id="sib1" style="border:2px solid;">
            <div id="sib2" data-bind="click: showHideNextSibling" >
            Click me
            </div>
            <div id="sib3" style="display: none;">
            Now you see me!
            </div>
            <div id="sib4">
            You should always see me.
            </div>
        </div>
        Option 3 - Boolean binding using a custom binding handler called slideToggle 
        <div id="bool1" style="border:2px solid;">
            <div id="bool2" data-bind="click: showHide" >
            Click me
            </div>
            <div id="bool3" data-bind="slideToggle: isVisible">
            Now you see me!
            </div>
        </div>
    </div>
</body>
</html>

HTML Search using MVVM with knockout.js

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://code.jquery.com/jquery-latest.pack.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
     
    <script type="text/javascript" >
        $(function(){
         
            // custom binding handler so pressing enter in a textbox is the same
            // as clicking the button.
            ko.bindingHandlers.enterKey = {
                init: function(element, valueAccessor, allBindings, vm) {
                    ko.utils.registerEventHandler(element, "keyup", function(event) {
                        if (event.keyCode === 13) {
                            ko.utils.triggerEvent(element, "change");
                            valueAccessor().call(vm, vm);
                        }                        
                        return true;
                    });
                }         
            };
         
            var fakeSearch = function(args, callback) {
                var data = args;
                callback(data);
                return args;
            };

            var ButtonViewModel = function(text, searchMethod, canSearchMethod) {
                var _self = this;
                _self._canSearchMethod = canSearchMethod;
                _self.text = ko.observable(text);
                _self.onClick = searchMethod;
                _self.canClick = ko.computed(_self._canSearchMethod);                
            };

            var SearchParametersViewModel = {
                'SearchTerm': ko.observable(''),
                'SearchOption': ko.observable(''), // Not used in example
                'StartDate': ko.observable(''),    // Not used in example
                'EndDate': ko.observable('')       // Not used in example
            };

            var SearchViewModel = function(searchMethod, searchArgs) {
                // private properties
                var _self = this;
                _self._searchMethod = searchMethod;
                _self._searchArgs = searchArgs;
                _self._canSearch = function() {
                    return _self.searchParameters.SearchTerm() != null && _self.searchParameters.SearchTerm() != '';
                };

                // public properties
                _self.searchParameters = SearchParametersViewModel;

                _self.results = ko.observable('');
                
                _self.searchCallBack = function (data) {
                    _self.results(JSON.stringify(data));
                };

                _self.searchButton = new ButtonViewModel("Search", 
                function () {
                    _self._searchMethod(_self._searchArgs, _self.searchCallBack);
                }, _self._canSearch);
                
            };
           
          ko.applyBindings(new SearchViewModel(fakeSearch, {a: "1", b: "2"}));
        });
    </script>
</head>
<body>
    <input data-bind="value: searchParameters.SearchTerm, valueUpdate: 'afterkeydown', enterKey: searchButton.onClick" />
    <button type="button" id="btnSearch" data-bind="text: searchButton.text, enable: searchButton.canClick, click: searchButton.onClick"></button>
    <p data-bind="text: results"></p>
</body>
</html>

ASP.NET, JavaScript, and HTML Element ID attributes

Today I had to fix some legacy code. It is ASP.NET code and it has both ASP.NET elements and ASP.NET Controls. The plan was to replace a large portion of code behind with JavaScript. The ASP.NET code needed to be a UserControl that could appear twice on the same page. This created some problems:

  1. ASP.NET creates some html controls but changes the id and the name attributes. While it is possible to run the web page and see what the attributes will be changed to and then use those strings statically in JavaScript, the JavaScript could easily break with slight changes to the ASP.NET code. This is not scalable or maintainable.
  2. ASP.NET does NOT rename the the id or the name attributes for normal HTML tags. First, that is a consistency issue. Second it is an issue using the same control multiple times. Third, if you want to get the value of a form element, doing so uses the name attribute and so each form element needs a separate name.

So lets explain the is problem with a real world scenario.

Lets say you have the following requirements:

  1. Create a UserControl, called PersonControl, that accepts person’s basic info: First name, Last name, and Birthdate.
  2. The form also has a button and it should only be enabled if all three fields are populated.
  3. The Birthdate should use JQuery’s DateTimePicker.
  4. The First name and Last name should be ASP.NET text boxes.

Now imagine the site already exists and you have to add this requirement:

  1. A web page should exist that has multiple PersonControls showing: for example: Employee, Emergency Contact.

The problem with ASP.NET, JavaScript, and HTML Element ID attributes

So let’s build this project using a single control and static id and name attributes and see how it works. Later we will see what we need to do to get this working with multiple PersonControls.

  1. Open Visual Studio and create a new ASP.NET Empty Web Application.
  2. Add JQuery and JQuery-UI. Do this as follows:
    1. Right-click on the project and choose Manage NuGet Packages.
    2. In the Manage NuGet Packages window, on the left, click Online.
    3. On the right, in the search field, type JQuery.
    4. Install JQuery and JQuery UI.
  3. Create new Web Form called PersonForm.
  4. Add the following into your PersonForm.aspx file:
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="PersonForm.aspx.cs" Inherits="AspAndJavaScriptExample.PersonForm" %>
    
    <%@ Register Src="~/PersonControl.ascx" TagPrefix="uc1" TagName="PersonControl" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>ASP.NET, JavaScript, and HTML id Attributes</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                <h2>Employee</h2>
                <uc1:PersonControl runat="server" ID="PersonControlEmployee" />
            </div>
            <%--<div>
                <uc1:PersonControl runat="server" ID="PersonControlEmergencyContact" />
            </div>--%>
        </form>
    </body>
    </html>
    
  5. Create a new Web User Control called PersonControl.
  6. Add the following into your PersonControl.ascx file:
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PersonControl.ascx.cs" Inherits="AspAndJavaScriptExample.PersonControl" %>
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PersonControl.ascx.cs" Inherits="AspAndJavaScriptExample.PersonControl" %>
    <link href="Content/themes/base/minified/jquery.ui.datepicker.min.css" rel="stylesheet" />
    <script src="scripts/jquery-2.0.0.min.js"></script>
    <script src="scripts/jquery-ui-1.10.3.min.js"></script>
    <script src="scripts/ButtonManager.js"></script>
    <script>
        $(function () {
            $(".datepicker").datepicker();
        });
    </script>
    
    <div style="border: solid; padding: 2px">
        First name:
        <asp:TextBox ID="FirstName" runat="server"></asp:TextBox><br />
        Last name:
        <asp:TextBox ID="LastName" runat="server"></asp:TextBox><br />
        Birthdate:
        <input type="text" name="BirthDate" id="BirthDate" class="datepicker" /><br />
        <asp:Button ID="SubmitPerson" runat="server" Text="Submit" OnClick="SubmitPerson_Click" />
    </div>
    
  7. Add the following button SubmitPerson_Click method into your PersonControl.ascx file:
            protected void SubmitPerson_Click(object sender, EventArgs e)
            {   // Put break point here            
                var firstName = FirstName.Text;
                var laststName = LastName.Text;
                var birthdate = Request.Form["Birthdate"];
            }
    
  8. Now add this ButtonManager.js file.
    jQuery(document).ready(function () { 
        $("#PersonControlEmployee_FirstName").bind("propertychange keyup input paste", setButtonState);
        $("#PersonControlEmployee_LastName").bind("propertychange keyup input paste", setButtonState);
        $("#BirthDate").change(setButtonState);
        setButtonState();
    });
    
    var setButtonState = function () {
        if (!areValuesPopulated())
            $("#PersonControlEmployee_SubmitPerson").attr("disabled", "disabled");
        else
            $("#PersonControlEmployee_SubmitPerson").removeAttr("disabled");
    }
    
    var areValuesPopulated = function () {
        return $("#PersonControlEmployee_FirstName").val() != ""
             && $("#PersonControlEmployee_LastName").val() != ""
             && $("#BirthDate").datepicker().val() != "";
    }
    
  9. Now run the project and look at the source code of the html. It looks as follows:
    
    
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head><title>
    	ASP.NET, JavaScript, and HTML id Attributes
    </title></head>
    <body>
        <form method="post" action="PersonForm.aspx" id="form1">
    <div class="aspNetHidden">
    <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="nkizDAjcAtd96A9EOpli0xdG3n6zTXVaM/5t2fmcAI5+LPQ6OzzIV2wUpisxoUTMFxIKkUwKDY4Xk36/NouRsiE81gq5z3Ch/tz3DlxJW9g=" />
    </div>
    
    <div class="aspNetHidden">
    
    	<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="XCB0fxwKvdqEXpUBaICUtf6EzkAvBeahq0bywZekyukCuzvGVagqOnVHUWFHF2Cycd2xkg/UhNh/B3qNqabkBI+Flj52GkM3p6SF5eL/M4SnnfwmFBjWVOaTZ+IlwMvkR1bGuMxomyeJJ5HaU1FXWSYDYHVtgM9tdsVi31EireM=" />
    </div>
            <div>
                <h2>Employee</h2>
                
    <link href="Content/themes/base/minified/jquery.ui.datepicker.min.css" rel="stylesheet" />
    <script src="scripts/jquery-2.0.0.min.js"></script>
    <script src="scripts/jquery-ui-1.10.3.min.js"></script>
    <script src="scripts/ButtonManager.js"></script>
    <script>
        $(function () {
            $(".datepicker").datepicker();
        });
    </script>
    
    <div style="border: solid; padding: 2px">
        First name:
        <input name="PersonControlEmployee$FirstName" type="text" id="PersonControlEmployee_FirstName" /><br />
        Last name:
        <input name="PersonControlEmployee$LastName" type="text" id="PersonControlEmployee_LastName" /><br />
        Birthdate:
        <input type="text" name="BirthDate" id="BirthDate" class="datepicker" /><br />
        <input type="submit" name="PersonControlEmployee$SubmitPerson" value="Submit" id="PersonControlEmployee_SubmitPerson" />
    </div>
    
            </div>
            
        </form>
    </body>
    </html>
    

Problems

  1. Notice the id and name attributes on the tags. The ASP.NET controls have been altered by ASP.NET with a prefix. This is not the problem. This is good. If the control is used multiple times, then this keeps the id and name attributes unique and they are supposed to be unique. However, the problem is, if the id is changed in this line . . .
    <uc1:PersonControl runat="server" ID="PersonControlEmployee" />
    

    . . . then id and name attributes in the child control will change. Since we are using those values statically in the ManageButton.js, any such change also breaks in the javascript. Also, we aren’t using a master page, but if you decided to add a master page, that add an additional prefix, which would create different id and name attributes, again causing the javascript to break. In fact, any such nesting change will change the id and name attributes breaking the javascript.

  2. The control that is not an ASP.NET control, the JQuery datepicker control, did not have the same modifications made to the Birthdate. So this tag won’t work if the control is used multiple times.

Do you want to see the problem?

Update your form in PersonForm.aspx to include multiple controls.

    <form id="form1" runat="server">
        <div>
            <h2>Employee</h2>
            <uc1:PersonControl runat="server" ID="PersonControlEmployee" />
        </div>
        <div>
            <h2>Emergency Contact</h2>
            <uc1:PersonControl runat="server" ID="PersonControlEmergencyContact" />
        </div>
    </form>

Now give it try. See the problems? Ok. So now you have a simulation of the problematic code that I faced today.

Solution to ASP.NET, JavaScript, and HTML Element ID attributes

So we are going to fix this in parts. We are going to use a variable in the ASP.NET control called ClientID, that is basically the prefix used.

  1. Fix html elements to use the same prefix as the ASP.NET controls
  2. Fix the javascript to receive the ClientID as a parameter
  3. Fix the datepicker attributes

    First, let’s fix the ASP.NET and HTML so that all the id and name attributes are consistently changed.

    Change the datepicker line and add some code so it will have the same prefix as ASP.NET created html controls.

    <input type="text" name="<%=ClientID%>$BirthDate" id="<%=ClientID%>_BirthDate" class="datepicker" /><br />
    

    Pass the prefix into the JavaScript

    1. In the PersonControl.ascx file, add a little snippet of JavaScript code to pass the ClientID into the JavaScript files.
      <script>
          $(function () {
              $(".datepicker").datepicker();
          });
          jQuery(document).ready(function () {
              startManagingButton("<%=ClientID%>");
          });
      </script>
      
    2. Now update your JavaScript file to use that ClientID as the prefix. Notice, this is used in the events. I created a simple buildId method that I use throughout now.
      var startManagingButton = function (inIdPrefix) {
          $(buildId(inIdPrefix, "_FirstName")).bind("propertychange keyup input paste", inIdPrefix, setButtonState);
          $(buildId(inIdPrefix, "_LastName")).bind("propertychange keyup input paste", inIdPrefix, setButtonState);
          $(buildId(inIdPrefix, "_BirthDate")).change(inIdPrefix, setButtonState);
          setButtonState(inIdPrefix);
      }
      
      var setButtonState = function (inIdPrefix) {
          if (inIdPrefix.data)
              inIdPrefix = inIdPrefix.data;
          
          if ($(buildId(inIdPrefix, "_FirstName")).val() == "" || $(buildId(inIdPrefix, "_LastName")).val() == "" || $(buildId(inIdPrefix, "_BirthDate")).val() == "")
              $(buildId(inIdPrefix, "_SubmitPerson")).attr("disabled", "disabled");
          else
              $(buildId(inIdPrefix, "_SubmitPerson")).removeAttr("disabled");
      };
      
      var buildId = function (inIdPrefix, idSuffix) {
          return "#" + inIdPrefix + idSuffix;
      };
      

      Now no matter how you update or move this web page, the Id values always work.

    Use the ClientID in ASP.NET SubmitPerson_Click method

    Update the code to look as follows:

        protected void SubmitPerson_Click(object sender, EventArgs e)
        {   // Put break point here            
            var firstName = FirstName.Text;
            var laststName = LastName.Text;
            var birthdate = Request.Form[ClientID + "$Birthdate"];
        }
    

    Using the PersonControl multiple times

    Now everything should be working and you should be able to include as many instances of your control in your web page as you want.

    Update your form in PersonForm.aspx to include many of these controls.

        <form id="form1" runat="server">
            <div>
                <h2>Employee</h2>
                <uc1:PersonControl runat="server" ID="PersonControlEmployee" />
            </div>
            <div>
                <h2>Spouse</h2>
                <uc1:PersonControl runat="server" ID="PersonControlSpouse" />
            </div>
            <div>
                <h2>Emergency Contact</h2>
                <uc1:PersonControl runat="server" ID="PersonControlEmergencyContact" />
            </div>
        </form>
    

    Now give it try. All instances of the PersonControl are now working. The dynamic id and name attributes are not a problem as we are handling them; in fact, they are part of the ultimate solution to make the control reusable.

    Conclusion

    ASP.NET, JavaScript, and HTML Element ID attributes can all be used to work together to make a nice cohesive application.

    If you have a better solution, please post a comment and let me know.

    Downloads

    Here is the project in both states:

    AspAndJavaScriptExample-problematic.zip

    AspAndJavaScriptExample-working.zip

    Bugs

    Now the only bug I can find is on clicking submit the JQuery datepicker field is cleared. I’ll try to fix that and post the solution in another post.

Using knockout.js and MVVM with RadioButtons

In the example below, radio buttons selection list is created by binding to a list in the view model. I show how to bind to both the selected radio button’s value and name. I also show how to disable a button unless the radio button is selected.

<!-- Step 1 - Create the HTML File or the View -->
<!DOCTYPE html>
<html>
<head>
    <!-- Step 2 - Include jquery and knockout -->
    <script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="scripts/knockout-2.2.1.js"></script>
    
    <!-- Step 3 - Add script to run when page loads -->
    <script type="text/javascript" >
        $(document).ready(function(){

			<!-- Step 4 - Add a RadioButtonModel -->
            function RadioButtonModel(inText, inValue, inGroupName) {
                this.text = inText;
				this.value = inValue;
				this.name = inGroupName;
            }

			<!-- Step 5 - Create ViewModel -->
            function SurveyViewModel() {
                var self = this;
                
				<!-- Step 7 - Create an observable list -->
                this.list = ko.observableArray([
                    new RadioButtonModel("Good", 10, "Q1"),
                    new RadioButtonModel("Average", 5, "Q1"),
                    new RadioButtonModel("Poor", 1, "Q1")
                ]);
				
				<!-- Step 8 - Create a selected item -->
				this.selected = ko.observable(0);
				
				<!-- Step 9 - (Optional) Create a computed value to get the selected text -->
				this.selectedtext = ko.computed(function() {
					var text = "";
				    ko.utils.arrayForEach(this.list(), function(item) {
						var selectedItem = self.selected();
						if (selectedItem == item.value)
							text = item.text;
					});
					return text;
				}, this);
				
				
				<!-- Step 10 - Create a computed value to require a selection before submitting -->
			    this.canClick = ko.computed( function() {
                    return this.selectedtext() != "";
                }, this);
                
            }
            
            <!-- Step 10 - Create a computed value to require a selection before submitting -->    
            ko.applyBindings(new SurveyViewModel());
        });
    </script>
</head>
<body>
    <!-- Step 11 - Create a div containing the html for one radio button and bind to foreach: list -->
    <div data-bind="foreach: list">
        <input type="radio" data-bind="attr: {name: name}, value: value, checked: $root.selected" /><span data-bind="text: text"></span>
    </div>

    <!-- Step 12 - Add html elements to see other properties -->
    <p data-bind="text: selected"></p>
    <p data-bind="text: selectedtext"></p>
    
    <!-- Step 13 - Add a button and bind its enable state -->
    <button type="submit" id="btnSubmit" data-bind="enable: canClick">Submit</button>

</body>
</html>

Hope this helps you.