I call ajax to draw a table from the mysql datas and put textareas below that table to be able to write a documentation for each fields.
Here's my problem. After I type the table name to the search field and click the button, I get the whole page as I wished. After I search for the same table name, I get the same page again, but after I check the same table the third time, I only get two tables being drew. The same issue occurs if I check 'test' table once, then 'test2' twice.
UPDATE: After the second ajax call (so if you type something and click to check meanwhile an other one is in the browser) the jQuery plugin only load the mysqlTable template and doesn't the textareas. Despite this textareas still appear, but if you call the third time any table then the issue occurs, the success function stops executing half-way and I only see two tables without textareas.
What I noticed is that the third time it calls ajax, it only executes until that part where I left the comment /* *** this is the point where ... That's the reason why textareas and other does not appear.
It supposed to be like this.
But it looks like this after the third call.
I disable the search input while loading, in order to avoid multiple ajax call, but something happens here.
JS ajax call
function templatesHandling(){
clear();
$('#textareaHolder').html('');
$.ajax({
url: "ajax/table.php?function=get_table_data&table="+tableName,
dataType: "json", // it'll convert json to objects
type : "GET",
beforeSend : function(){ $('#ajax-loader-gif').show(); $('#search').attr({'disabled':'disabled','placeholder':'Loading...'});},
complete : function(){ $('#ajax-loader-gif').hide(); $('#search').removeAttr('disabled').prop('placeholder','Type the table name..'); },
error: function(er){ $("#check").after("<span id='error'>Error</span>"); },
success: function(data){
// add the current table name to the first object in order to be able to print out with {{tableName}}
if (!data[0].TableName){
data[0].TableName = tableName;
}
// call the 'table' template and send the data to work with
$("#tableHolder").loadFromTemplate({
template : "mysqlTable",
data : data
});
/* *** this is the point where sometimes the ajax call stops executing *** */
// call the 'textareas' template and send the data to work with
$("#textareaHolder").loadFromTemplate({
template : "textareas",
data : data
});
/* *** parseWiki *** */
$('#parseWikiHolder').show(); // show the last textarea
for (key in data[1]){
res_head[++j] = '!scope="col"| '+ key + '\n'; //
}
for(var i=1;i<data.length;i++){
for(key in data[i]){
res_body[++j] = '|'+data[i][key]+'\n';
}; // -for in | parse <tbody>
res_body[++j] = '|-\n';
}; // -for | parse <tbody>
} // -success
});
};
I use jQuery HandlebarsJS to load Handlebars templates from separated files.
Do you have any thought about this issue? Why does it happen?
(let me know if more code needed)
UPDATE
I inserted the full JS code of mine to JSFiddle, click here!
textareas (template)
{{#each this}}
{{#if TableName}}
<label for='{{TableName}}'>Description of <em class="tableName">{{TableName}}</em> table</label>
<textarea id='{{TableName}}'></textarea>
{{/if}}
{{#if Field}}
<label for='{{Field}}'>Description of <em class="tableName">{{Field}}</em> field</label>
<textarea id='{{Field}}'></textarea>
{{/if}}
{{/each}}
mysqlTable (template)
<table>
<thead>
<tr>
{{#each this}}
{{#if TableName}}
<th class="structure_thead" colspan="6">Structure of {{TableName}} table</th>
{{/if}}
{{/each}}
</tr>
<tr>
<th>Field</th>
<th>Type</th>
<th>Null</th>
<th>Key</th>
<th>Default</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
{{#each this}}
{{#if Field}}
<tr>
<td><span class="ui-icon ui-icon-link"></span>{{Field}}</td>
<td>{{Type}}</td>
<td>{{Null}}</td>
<td>{{Key}}</td>
<td>{{Default}}</td>
<td>{{Extra}}</td>
</tr>
{{/if}}
{{/each}}
</tbody>
</table>
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Doksi</title>
<link rel="stylesheet" type="text/css" href="css/styles.css">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
<link href='http://fonts.googleapis.com/css?family=Vollkorn' rel='stylesheet' type='text/css'>
</head>
<body>
<div class="container">
<div class="searchHolder">
<input id="search" type="text" placeholder="Type the table name.."/>
<input id="check" type="button" value="Check" />
<img id="ajax-loader-gif" src="img/ajax-loader.gif" />
</div>
<div id="tableHolder"></div>
<div id="textareaHolder"></div>
<div id="parseWikiHolder">
<input type="text" disabled="disabled" value="Ready to copy?"/>
<input id="copy" type="button" value="Hell, yeah!" />
<textarea id="parseWiki"></textarea>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.loadFromTemplate.js"></script>
<script src="js/ajax.js"></script>
</body>
</html>
Get the table name - JSON file I receive
[
{
table: "teszt"
},
{
table: "teszt2"
}
]
Get the table's data - JSON file I receive
[
{
eredmeny: "siker"
},
{
Field: "id",
Type: "int(11)",
Null: "NO",
Key: "PRI",
Default: null,
Extra: "auto_increment"
},
{
Field: "name",
Type: "varchar(64)",
Null: "NO",
Key: "",
Default: null,
Extra: ""
},
{
Field: "type",
Type: "enum('y','n')",
Null: "NO",
Key: "",
Default: "n",
Extra: ""
},
{
Field: "date",
Type: "datetime",
Null: "NO",
Key: "",
Default: null,
Extra: ""
}
]
UPDATE 2
I commented out that jQuery plugin and tried a simple ajax call, but the same issue occurs. If I run this code, just the table will appear. If I change the code as textareas are in the first place, then only they will appear. Why does the code stops executing without any error? Are the two ajax call kick each other out?
The new ajax calls
(function getTemplateAjax(path) {
var source;
var template;
$.ajax({
url: path, //ex. js/templates/mytemplate.handlebars
cache: true,
success: function(data) {
source = data;
template = Handlebars.compile(source);
$('#tableHolder').html(template(o));
}
});
})('templates/mysqlTable.html')
(function getTemplateAjax2(path) {
var source;
var template;
$.ajax({
url: path, //ex. js/templates/mytemplate.handlebars
cache: true,
success: function(data) {
source = data;
template = Handlebars.compile(source);
$('#textareaHolder').html(template(o));
}
});
})('templates/textareas.html')
I found a (half)solution
As I copied that new code block to below UPDATE2, after I changed that again and found the (half)solution.
...
success: function(o){
// add the current table name to the first object in order to be able to print out with {{tableName}}
if (!o[0].TableName){
o[0].TableName = tableName;
};
// call the 'mysqlTable' template and send the data to work with
(function getTemplateAjax(objectum, path, element){
var source, template;
$.ajax({
url: path,
cache: true,
success: function(data) {
source = data;
template = Handlebars.compile(source);
element.html(template(objectum));
}
});
})(o, 'templates/mysqlTable.html', $('#tableHolder'));
// call the 'textareas' template and send the data to work with
(function getTemplateAjax(objectum, path, element){
var source, template;
$.ajax({
url: path,
cache: true,
success: function(data) {
source = data;
template = Handlebars.compile(source);
element.html(template(objectum));
}
});
})(o, 'templates/textareas.html', $('#textareaHolder'));
...
It's quite ugly and with more template files it won't the best practice, so I'll look for a better solution. At least it works now.
So the bug was probably inside the plugin. I am going to write my own jQuery plugin for this task.
I think the problem is with the async mode for the ajax call for that reason I just commited a new changes you can take a look into the examples:
https://github.com/CKGrafico/jQuery_HandlebarsJS/commit/41eb4c7c39f9c1ef8bbd1015b594d0a33bb585ac maybe this could fix your problem.
Related
I want to add automatic Intellisense (Auto Complete--Filtering Search Result) to a textbox, corresponding to the words that I'm typing in that textbox and the Intellisense is fetched from a database table. How can I achieve this? Can anyone help?
Here is my jQuery code:
$(document).ready(function() {
$('#city').autocomplete({
source:'send.php'
});
});
send.php file given below:
$link=mysqli_connect("localhost","hari","123","hari");
$searchTerm = $_GET['query']; //get search term
$query = $db->query("SELECT fname FROM user WHERE fname LIKE
'%".$searchTerm."%' ORDER BY fname ASC"); //get matched data from user table
while ($row = $query->fetch_assoc()) {
$data[] = $row['fname'];
}
echo json_encode($data);//return json data
Corresponding HTML Code is given below:
<div class="content col-sm-12">
<form>
<h1>Hello!!!</h1>
<input type="text" id="city" name="city" size="20" class="city"
placeholder="Please Enter City or ZIP code"><br><br>
</form>
</div>
You have to include the following scripts in your html page
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
And add the following css in head of your html
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
The mistake you made is the parameter passing with the name term and trying to read with the name query in your php file. In your send.php file change the line
$searchTerm = $_GET['query'];
into
$searchTerm = $_GET['term'];
Try this:
$(document).ready(function() {
$('#city').autocomplete({
source: function( request, response ) {
$.ajax( {
url: "send.php",
dataType: "jsonp",
data: {
query: request.term
},
success: function( data ) {
response( data );
}
} );
},
});
});
I have a recommendation for you, use angular 1 for this, you can simply write that code without additional UI libraries and with much much better performance and issue-free solution.
Add the following parent div to your input element:
Change your input to this:
<input type="text" id="city" name="city" size="20" class="city" ng-model="query" ng-change="fetch()" placeholder="Please Enter City or ZIP code">
Add the following code right under your <input>:
<ul>
<li ng-repeat="text in suggestions">{{ text }}</li>
</ul>
As a basic set up, you need this in your <head> section:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
Finally you will create a new file like in assets directory like "suggestions.js" in your assets directory or somewhere and add this file right before you your </body> tag like this in your template:
<script src="assets/suggestions.js"></script>
The file will have the following lines:
var app = angular.module('myApp', []);
app.controller('suggestionsCtrl', function($scope, $http) {
$scope.suggestions = [];
$scope.query = '';
$scope.fetch = function() {
$http({method: 'POST', url: 'send.php', params: { query: $scope.query } }).
then(function(response) {
$scope.status = response.status;
$scope.suggestions = response.data;
}, function(response) {
/* SOMETHING WENT WRONG WTIH THE CALL DO SOMETHING HERE */
});
};
});
There is very simple set-up/tutorial for Angular 1 here:
https://www.w3schools.com/angular/default.asp
This is not a direct answer but believe me a more efficient answer. Angular 1 and the newer versions save a lot of time and brings performance.
And btw, autocomplete() is not a native jQuery function. Also I do not mention that you need jQuery also for Angular, but I assume it's already added in your template.
Hi I'm learning AngularJS and I have a question. I want to display a table on a button click. On clicking the button, JSON data gets fetched but I have to press the button twice for the data to be displayed.
This is the HTML page.
<html>
<body>
<div class="container">
<label for="tags" style="margin-top: 30px;margin-left: 15px;">GSTIN </label>
<input id="tags">
<button ng-click="searchfunction()">search</button>
</div>
<br/>
<hr>
<div class="container">
<div ng-show="tshow" ng-repeat="x in searchdata">
<table class="table table-bordered table-responsive">
<thead>
<tr>
<th>MON</th>
<th>SGST</th>
<th>CGST</th>
<th>IGST</th>
<th>CESS</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in x">
<td>{{i.mon}}</td>
<td>{{i.sgst}}</td>
<td>{{i.cgst}}</td>
<td>{{i.igst}}</td>
<td>{{i.cess}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
This is the controller:
app.controller("searchcontroller", function ($scope,$http) {
$scope.tshow=false;
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
$scope.searchfunction=function() {
$scope.tshow=true;
var tf=document.getElementById("tags");
var value=tf.value;
var auth = make_base_auth("gstadmn112","Gstn#123");
var url6 = "http://164.100.148.67:8080/wsgstr3B/rest/payment/gstinsearch?gstin="+value+"&year=201718";
xml = new XMLHttpRequest();
// jQuery
$.ajax({
url : url6,
method : 'GET',
beforeSend : function(req) {
req.setRequestHeader('Authorization', auth);
},
success:function(response) {
console.log(response);
scope.searchdata=response;
},
failure:function() {
window.alert("wrong input data doesn't exist");
}
});
}
});
I need to click twice on the search button for the table to be displayed. I want the table to be hidden initially and once the search is successful the table should be displayed. The table is hidden initially and after clicking twice correct data gets displayed.
Maybe, you try to add $scope.tshow=true; in function success:
success:function(response) {
console.log(response);
$scope.tshow=true;
$scope.searchdata=response;
},
P.S. I advise to use $http instead of $ajax.
This problem is related to the digest loop of angularjs which keeps all changes sync between your view and controller.
When you invoke the searchfunction(), angularjs will know whats happening inside the method and sync the changes made with the view when its completed.
The problem is that your method uses $.ajax which has some async callback methods.
When these methods gets invoked angularjs have already left the party (digest loops is over) and don't know what these methods have done to your controller $scope.
The jQuery success callback will however set the $scope.searchdata=response; and this change gets notified the next time angularjs is in the party (the next time you click).
So basically you need to make sure angularjs is aware of the async methods which makes changes to your $scope.
To solve this I would inject angularjs own $http service (which takes care of async changes to the scope) and use that instead.
var req = {
method: 'GET',
url: url6,
headers: {
'Authorization': auth
}
}
$http(req).then(function(response){
console.log(response);
$scope.searchdata=response;
}, function(){
window.alert("wrong input data doesn't exist");
});
You can use this way.
$scope.searchfunction=function(){
$scope.tshow=true;
var tf=document.getElementById("tags");
var value=tf.value;
$http.get("http://164.100.148.67:8080/wsgstr3B/rest/payment/gstinsearch?gstin="+value+"&year=201718")
.success(function(result) {
$scope.searchdata=response;
$scope.tshow=false;
})
.error(function() {
window.alert("wrong input data doesn't exist");
});
}
I'm trying to store user input in a javascript array and send it to controller via ajax call. But all I get in controller's parameter is null.
Here's the code:
<table class="table-condensed table-bordered table-striped table-responsive">
#foreach (var project in projects)
{
<tr>
#foreach (var parameter in parameters)
{
<td>
<input type="text" class="form-control remar" id=#i />
</td>
i++;
}
</tr>
}
<tr>
<td colspan=#(parameters.Count() + 1)>
<button class="btn btn-primary pull-right" onclick="insert()">Submit Remarks</button>
</td>
</tr>
</table>
<script>
function insert() {
var remarks = [];
jQuery(".remark").each(function () {
remarks.push(jQuery(this).val());
});
$.ajax({
type: "POST",
url: "#Url.Action("AddRemarksToEvaluationSheet", "Teacher")",
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
}
</script>
Controller:
public ActionResult AddRemarksToEvaluationSheet(string[] function_param)
{
return View();
}
Any help?
P.S. the above code is edited. It worked!
You've got lots going on here...
First, don't give your input boxes ids of numbers - in this scenario, it doesn't look like you even use the value...But if you need it, put the value into a data element:
<input type="text" class="form-control remark" data-remark-id="#i" />
When retrieving the values, you need to get the value, not the textbox itself:
var remarks = [];
jQuery(".remark").each(function() {
remarks.push(jQuery(this).val());
});
When doing anything weird with parameters, like arrays or complex objects, if you use JSON instead of the default of URL-encoded, it will make things nicer.
You should also avoid absolute paths, and use Url.Action instead, so that it'll work regardless of where your app lives relative to the domain.
$.ajax({
type: "POST",
url: "#Url.Action("AddRemarksToEvaluationSheet", "Teacher")",
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
And you can accept an array of strings, rather than of objects:
[HttpPost]
public ActionResult AddRemarksToEvaluationSheet(string[] function_param)
{
}
I have a feeling that you aren't getting the remarks in the array in the first place.
If you aren't already, use a browser that allows you to debug the js. If you use Chrome, right-click -> inpsect element (or F12). Go to the 'Sources' tab, go to your js file and put a break point to see what the remarks array looks like.
Regarding the code:
You do not seem to need id attributes on the inputs. Let alone numerical ids.
To populate the remarks array, get all dom elements having the class you placed on all inputs. For each one, push the value in the array.
var remarks = [];
$(".form-control").each(function() {
remarks.push($(this).val());
});
You can add extra code to only add the ones with value.
var remarks = [];
$(".form-control").each(function() {
if($(this).val().length){
remarks.push($(this).val());
}
});
The ajax call:
$.ajax({
type: "POST",
url: addRemarksUrl,
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
Where addRemarksUrl can be a global variable declared in the html.
There are other ways of getting the url. Have a look here:
How to send razor created Url to js file
This user offers 3 possible solutions:
global js variable
custom "data-" attribute
hidden input
I have a Index.html file, where I am using container class.
I have another html file with which contains mustache variables.
Here is the code which I am using,
Lets say this is a.html.
<script id="filtersOptions" type="text/html">
<ul class="checkboxCommonContent">
{{#data}}
<li>
<div>
<input type="checkbox" id="checkbox-1-1" class="regular-checkbox"><label for="checkbox-1-1"></label><span class="lblText">{{brand_name}}</span>
</div>
</li>
{{/data}}
</ul>
I have a json file, where the brand information something like this,
{
"brands":[
{
"brand_name":"Adidas",
"available_products":30
}
]
}
Through Javascript I am featching the Json data and trying to udapete the mustache tempalte but getting error.
Featchng information from js
loadFileForFilters: function(){
$.getJSON('js/json/brand.json', {}, function(data, textStatus, jqXHr) {
console.log(data);
var f = $("#filtersOptions").html();
$.get('files/sort_and_filter_lb.html', function(template, textStatus, jqXhr) {
var template = Mustache.render(f, {data: data});
//$(".container").html(template);
});
});
}
container - Is in side index.html.
The sort_and_filter_lb.html file have following code
<script id="filtersOptions" type="text/html"><ul class="checkboxCommonContent"> {{#data}} <li> <div> <input type="checkbox" id="checkbox-1-1" class="regular-checkbox"><label for="checkbox-1-1"></label><span class="lblText">{{brand_name}}</span> </div> </li> {{/data}} </ul> </script>
Can some one please guide me. Why I am not getting the data in the main template.
Edit,
Browsed some documentations MUSTACHE MANUAL , and demonstrations A Simple Mustache Demo , along with re-reading Question , for introduction into Mustache.js .
At first glance , appear that json object at brand.json does not have data property to correspond to {{#data}} ; see http://mustache.github.io/mustache.5.html#Sections at template and hash of {{#repo}}.
Not certain about necessity of second ajax call, i.e., $.get() ? Existing #filtersOptions (f) html could be modified , to reduce ajax call to first , $.getJSON() ?
Above portions not directly addressed here , though ajax pieces re-arranged to process their respective return values within .then() callback .
Changed <script> element at sort_and_filter_lb.html file to <div> , for jsfiddle to process .
Note, Not previously tried Mustache.js
Try
v2
html
<script id="filtersOptions" type="text/html">
<ul class="checkboxCommonContent"> {{#data}}
<li> <div> <input type="checkbox"
id="checkbox-1-1"
class="regular-checkbox" /> <label
for="checkbox-1-1"> </label><span class="lblText">{{brand_name}}</span> </div>
</li> {{/data}}
</ul>
</script>
<div class="process">
<button>Process Template</button>
</div>
<div id="container"></div>
js
$(function () {
$(".process button").on("click", function () {
var f = $('#filtersOptions').html();
var _data = {
"brands": {
"data": [{
"brand_name": "Adidas"
}, {
"available_products": 30
}]
}
};
var file = String('<div id=filtersOptions type=text/html>'
+'<ul class=checkboxCommonContent> {{#data}} <li>'
+'<div>'
+'<input type=checkbox id=checkbox-1-1 class=regular-checkbox>'
+'<label for=checkbox-1-1></label>'
+'<span class=lblText>{{brand_name}}</span>'
+'</div> </li> {{/data}} </ul> </div>');
var request1 = $.post("/echo/json/", {
json: JSON.stringify(_data)
}, function (data, textStatus, jqxhr) {
return data.brands;
});
var request2 = $.post("/echo/html/", {
html: file
}, function (data, textStatus, jqxhr) {
return data
});
$.when(request1, request2)
.then(function (a, b) {
console.log(a[0], b[0]);
var html = Mustache.render(b[0] /* or , `f` */, a[0].brands);
$('#container').html(html);
})
});
});
jsfiddle http://jsfiddle.net/guest271314/uhf73/
Your code appears a little messed up but close. If you want to render a template using an external file and external data, try something like this:
$.getJSON('js/json/brand.json', {}, function(data) {
// on success, request template
$.get('files/sort_and_filter_lb.html', function(template_string) {
// when we have both template and data, render it
var output = Mustache.render(template_string, {data: data});
$(".container").html(output);
});
});
I'm creating an app using the very slick KnockoutJS library, but I've run into a snag. On the html page, I have a plain <select> control that I want to load with JSON data returned from a web service.
I define the observable array as follows:
var laborRow = function () {
this.positions = ko.observableArray([]);
};
When the page loads, the ajax call is made and the data is returned. In the callback, I do the following:
success: function (msg) {
laborRow.positions = msg;
}
based on the KO docs, I would expect that I would set the result like this:
laborRow.positions(msg);
However, that just throws an error stating that "laborRow.positions in not a function"
The template in the html is as follows:
<tbody data-bind='template: {name: "laborRowTemplate", foreach: laborLine}'> </tbody>
</div>
<script type="text/html" id="laborRowTemplate">
<tr>
<td><select data-bind='options: positions, optionsText: "Title", optionsCaption: "select", value: selectedPosition '></select></td>
</tr>
</script>
The laborRow object is a property on the ViewModel which is bound to the page. For whatever reason, this does not work. To add another wrinkle, if I add code to peek into the observableArray and print out some piece of data, the data is in there. So it is being loaded successfully.
Any thoughts would be greatly appreciated.
The full code for my example case:
var laborRow = function () {
this.positions = ko.observableArray([]);
};
var projectEstimate = function () {
this.laborLine = ko.observableArray([new laborRow()]);
};
var projectViewModel = new projectEstimate();
ko.applyBindings(projectViewModel);
//and the code in the callback function on ajax success
success: function (msg) {
laborRow.positions = msg;
//laborRow.positions(msg); **this does not work - error is laborRow.positions is not a function**
},
And the html:
<tbody data-bind='template: {name: "laborRowTemplate", foreach:
laborLine}'> </tbody>
<script type="text/html" id="laborRowTemplate">
<tr>
<td><select data-bind='options: positions, optionsText:
"Title", optionsCaption: "select", value: selectedPosition '></
select></td>
</tr>
</script>
Finally, thanks to Sean's comments below, I was able to get it working by modifying the code in the callback as follows:
success: function (msg) {
projectViewModel.laborLine()[(projectViewModel.laborLine().length-1)].positionList(msg);
}
The problem is that you haven't actually created your model:
var laborRow = function () {
this.positions = ko.observableArray([]);
// will only be called if you call var some_var = new laborRow()
};
Change your function to a bare object (as shown in the Knockout docs):
var laborRow = {
positions: ko.observableArray([])
};
And you'll be able to call laborRow.positions(msg); and have it work.
EDIT
Based on the new code, laborRow is still not instantiated -- if you are setting var laborRow somewhere else in your code (around the ajax request, perhaps) then you'll want to make sure that your call stack looks like this:
projectViewModel.laborLine()[0].positions()
// This will return the array you're looking for.
// The key is that laborLine is a `getter` not an attribute
I've been bitten by the "ko variables are getters not attributes" bug on several occasions ... might that be happening with your code?