ajax tests

testing ko.toJS and $.ajax

by Jorgelig

HTML

<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="http://twitter.github.com/bootstrap/assets/js/bootstrap-tooltip.js"></script>
<script src="http://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<script src="https://github.com/downloads/rniemeyer/knockout-sortable/knockout-sortable.js"></script>
<div class="widget">
    <div class="person_editor">
        <div class="header" data-bind="with:person">
            <h1>Edit Person</h1>
            <span data-bind="text: id"></span>:
            <span data-bind="text: name"></span>
            <span class="spinner" data-bind="visible:$root.isSearching">Running AJAX...</span>
        </div>
        
        <div class="body">
            <form data-bind="submit:savePerson, with:person">
                <div class="field control-group" data-bind="dirty:name">
                    <label>Name</label>
                    <div><input type="text" data-bind="value:name, valueUpdate:'afterkeydown'" placeholder="Enter person name" /></div>
                </div>
                <div class="field control-group" data-bind="dirty:pushStatus">
                    <label>Push Status</label>
                    <div>
                        <select data-bind="
                            optionsCaption: 'Choose...',
                            options: push_status_values,
                            optionsValue: 'id',
                            optionsText: 'name',
                            value: pushStatus"></select>
                    </div>
                </div>
                <div class="field control-group" data-bind="dirty:hasHeadshot">
                    <label>Has Headshot</label>
                    <div>
                        <input type="checkbox" data-bind="checked: hasHeadshot" value="Y"/>
                    </div>
                </div>
                <div class="field control-group" data-bind="dirty:biography">
        ...

CSS

body {
    padding: 5px;
    margin: 5px;
    font-family: "helvetica", sans-serif
}
.spinner {
    XXXdisplay:none;
    margin-left:3px;
}
.widget {
    border: 5px solid lightblue;
    border-radius: 1em;
    -moz-box-shadow:    3px 3px 5px black;
    -webkit-box-shadow: 3px 3px 5px black;
    box-shadow:         3px 3px 5px black;
    --width:20em;
    min-height:100px;
}
.widget .header {
    display:block;
    padding:5px;
    border-top-left-radius:0.7em;
    border-top-right-radius:0.7em;
    border-bottom: 1px solid lightblue;
    background: -moz-linear-gradient(top, #f2f6f8 0%, #d8e1e7 50%, #b5c6d0 51%, #e0eff9 100%); /* FF3.6+ */
}
.widget .header h1 {
    display:inline;
    marin-right: 2em;
    font-weight:bold;
    font-size:150%;
}
.widget .header form {
    display:inline;
}
.widget .header .searchStatus {
    font-size: 75%
}
.widget .body {
    display:block;

}
.widget textarea {
    Xborder-radius: 3px 3px 3px 3px;
    padding: 0.3em;
    width: 100%;
    height: 100%;

}


.widget .field {
    width: 100%;
}
.widget .field:before, .widget .field:after {
    display: table;
    content: "";
}
.widget .field:after {
    clear: both;
}
.widget .field label,  .widget .field div {
    display: block;
    float: left;
    height: 100%;
    Xwidth: 10em;
    Xpadding: 0.3em;
    padding-left: 0.3em;
    padding-right: 0.3em;
    Xmargin-bottom: 0.5em;
}
.widget .field label {
    text-align: right;
    border-right: 2px solid lightgrey;
    width: 5em;
}
.widget .field label:after {
    Xcontent: ':';
}
.widget .field div {
    text-align: left;
    border-left: 2px solid lightgrey;
    margin-left:-2px;
    width: 25em;
}
.widget .sortable {
    padding: 0px;
    margin: 0px;
    list-style-type: none;
}
.widget .sortable-placeholder {
    border: 2px dotted grey;
    background-color: lightgrey;
}
.widget .sortable-item {
    border: 2px solid transparent;

}
.widget .sortable-item .handle {
    cursor: move;
    border: 2px dotted grey;
   ...

JavaScript

function ajaxGateway (watcher) {

    var self = this;
    var isRunning = watcher;

    // JSON method wrapper
    self.postAjaxData = function(validationCallback, url, data) {
        if (watcher) isRunning(true);
        //alert("firing json");
        // jQuery

        // $.post(url,data,validationCallback);
        console.log("Preparing AJAX data=",data);
        if(ko.isObservable(data)) data = ko.toJS(data);
        console.log("Sending AJAX data=",data);

        $.ajax({

            type: "POST",
            url: url,
            data: ko.toJS(data),
            contentType: "application/json",

            success: function(data, status) {
                // execute this code once the asynch JSON call completes
                if (watcher) isRunning(false);
                validationCallback(data, status);
            },
            error: self.handleJsonError
        });

    };

    self.handleJsonError = function (jqXHR, textStatus, errorThrown){
        if (watcher) isRunning(false);
        var msg = textStatus + (errorThrown) ? ": " + errorThrown : "";
        alert("Failure due to " + msg)
    };

};
  

function Person(data) {
    var self = this;
    ConvertMapToObservables(data, this); // use ko Mapping ext instead ?
    /*
    this.id = ko.observable(data.id);
    this.name = ko.observable(data.name);
    this.pushStatus = ko.observable(data.pushStatus);
    this.hasHeadshot = ko.observable(data.pushStatus);
    */
    self.addAlias = function(alias) {
        alias = $.trim(alias);
        self.aliases.push( { id:0, name:alias } );
    }
}

function PersonEditorViewModel(init_data) {
    // Data
    var self = this;
    self.person = ko.observable(new Person(init_data));
    self.push_status_values = ko.observableArray(push_status_values);
    self.alias = ko.observable("");
    self.isSearching = ko.observable(false);
    
    var slide_triggered_by_add = false; // flag to enable alias animations

    // custom Heavy Meta ajax object
   ...