Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Web and WebWorks Development

Reply
Regular Contributor
Posts: 65
Registered: ‎01-21-2011
My Device: Not Specified
Accepted Solution

html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

Hi,

 

I'm trying to create an application which runs on OS5 and OS6 and am using the html5_init.js scripts provided by RIM to get around the fact OS5 runs synchronously on gears and OS6 asynch on HTML5.. 

 

After much refactoring and re-writing i'm now left with the following stumbling block. On OS5 this works as i expect as described below BUT on OS6 it doesn't it behaves completely differently Smiley Sad

 

 

Example i create an array of objects and itterate over them and insert them in a test table.

 

function insertTestData() {
    db.transaction(function(tx) {
        var testData = [];
        for (var i = 0; i < 3; i++) {
            var testCodeValue = new Object();
            testCodeValue.code = "code" + i.toString();
            testCodeValue.value = "value" + i.toString();
            testData.push(testCodeValue);
        }

        for (var y = 0; y < testData.length; y++) {
            var code = testData[y].code;
            var value = testData[y].value;

            alert(code);
            alert(value);

            tx.executeSql("INSERT INTO test(code,value) VALUES (?,?)", [code, value], rowInserted, function(tx, err) { alert("Error!"); });
        }
    });
}

the end result is:

 

 

code: code0 value: value0
code: code1 value: value1
code: code2 value: value2

 

 

Great! but how do i handle the senario where i need  pass data from the executeSql calling method to result set i.e. I want to select a count of rows to see if i need to update or insert my new data... I have other more complicated senarios where i need to check some data first and based on the outcome do one with other another but this is a simple example.

 

When i try to reference the  other variables via a lambda result set handler it only process the last one.. and in this case does it 3 times on OS6.. On os5 it works a treat.

 

example

 

function updateIfExistsTestData(){
    db.transaction(function(tx) {
        var testData = [];
        for (var i = 0; i < 3; i++) {
            var testCodeValue = new Object();
            testCodeValue.code = "code" + i.toString();
            testCodeValue.value = "value" + (i+10).toString();
            testData.push(testCodeValue);
        }

        for (var y = 0; y < testData.length; y++) {
            var code = testData[y].code;
            var value = testData[y].value;

            alert(code);
            alert(value);

            tx.executeSql("SELECT * FROM test WHERE code = ?", [code], function(tx, rs) {
                if (rs.rows.length == 0) {
                    tx.executeSql("INSERT INTO test(code,value) VALUES (?,?)", [code, value], rowInserted, function(tx, err) { alert("Error!"); });
                }
                else {
                    tx.executeSql("UPDATE test SET value = ? WHERE code =?", [value, code], rowInserted, function(tx, err) { alert("Error!"); });
                }
            }, function(tx, err) { alert("Error!!") });
        }
    });
}

If performing an update to the previous command i would have expected to see:

 

code: code0 value: value10
code: code1 value: value11
code: code2 value: value12
but see this instead
code: code0 value: value0
code: code1 value: value1
code: code2 value: value12   (<- this row has been updated 3 times)
if i empty the table and run this update/insert command i get 
code: code2 value: value12
code: code2 value: value12
code: code2 value: value12
instead of 
code: code0 value: value10
code: code1 value: value11
code: code2 value: value12

So my question is, is there an easy way to access variables in the parent of a lambda without having to track things via global scoped variables?
Any help or suggetions would be appreciated. 
My whole example HTML page is as follows:

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
<html>
	<head>
	    <meta name="viewport" id="viewport" content="height=device-height,width=device-width,user-scalable=no" />
		<title>Untitled Page</title>
		<script>
		    var databaseName = "TEST";
		    var databaseDescription = "TEST";
		    var databaseSize = 1024 * 1024;
		    var db = window.openDatabase(databaseName, "", databaseDescription, databaseSize);
	    		    
		    function createTable() {
		        db.transaction(function(tx) {
		            tx.executeSql("CREATE TABLE IF NOT EXISTS test(code TEXT, value TEXT)", [], function(tx, resultSet) { alert("done"); }, function(tx, err) { alert("Error!"); });
		        });
		    }

		    function dropTable() {
		        db.transaction(function(tx) {
		            tx.executeSql("DROP TABLE IF EXISTS test", [], function(tx, resultSet) { alert("done"); }, function(tx, err) { alert("Error!"); });
		        });
		    }

            function updateIfExistsTestData(){
                db.transaction(function(tx) {
                    var testData = [];
                    for (var i = 0; i < 3; i++) {
                        var testCodeValue = new Object();
                        testCodeValue.code = "code" + i.toString();
                        testCodeValue.value = "value" + (i+10).toString();
                        testData.push(testCodeValue);
                    }

                    for (var y = 0; y < testData.length; y++) {
                        var code = testData[y].code;
                        var value = testData[y].value;

                        alert(code);
                        alert(value);

                        tx.executeSql("SELECT * FROM test WHERE code = ?", [code], function(tx, rs) {
                            if (rs.rows.length == 0) {
                                tx.executeSql("INSERT INTO test(code,value) VALUES (?,?)", [code, value], rowInserted, function(tx, err) { alert("Error!"); });
                            }
                            else {
                                tx.executeSql("UPDATE test SET value = ? WHERE code =?", [value, code], rowInserted, function(tx, err) { alert("Error!"); });
                            }
                        }, function(tx, err) { alert("Error!!") });
                    }
                });
            }

            function insertTestData() {
                db.transaction(function(tx) {
                    var testData = [];
                    for (var i = 0; i < 3; i++) {
                        var testCodeValue = new Object();
                        testCodeValue.code = "code" + i.toString();
                        testCodeValue.value = "value" + i.toString();
                        testData.push(testCodeValue);
                    }

                    for (var y = 0; y < testData.length; y++) {
                        var code = testData[y].code;
                        var value = testData[y].value;

                        alert(code);
                        alert(value);

                        tx.executeSql("INSERT INTO test(code,value) VALUES (?,?)", [code, value], rowInserted, function(tx, err) { alert("Error!"); });
                    }
                });
            }

		    function rowInserted(tx, rs) {
		        alert("row inserted");
		    }

		    function selectTestData() {
		        db.transaction(function(tx) {
		            tx.executeSql("SELECT code, value FROM test", [], function(tx, resultSet) {
		                if (resultSet.rows.length > 0) {
		                    for (var i = 0; i < resultSet.rows.length; i++) {
		                        var div = document.createElement("div");
		                        div.innerHTML = "code: " + resultSet.rows.item(i).code + " value: " + resultSet.rows.item(i).value;

		                        document.getElementById("tableContents").appendChild(div);
		                    }
		                }
		                else {
		                    document.getElementById("tableContents").innerHTML = "table Empty";
		                }
		            }, function(tx, err) { alert("Error!"); });
		        });
		    }
		    
		    
		</script>

	</head>
	<body>
	    <button onclick="dropTable()">Drop Table</button>
	    <button onclick="createTable()">Create Table</button>
	    <button onclick="selectTestData()">Select Test Data</button>
	    <button onclick="insertTestData()">Insert Test Data</button>
	    <button onclick="updateIfExistsTestData()">Insert Or Update If Exists</button>	       
	    
        <div id="tableContents"></div>
	</body>
</html>
Retired
Posts: 3,708
Registered: ‎10-16-2008
My Device: Z10
My Carrier: Rogers

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

You should be able pass state information and other data along with the function that you pass into your db.transaction.

 

Since a function is an object in JavaScript you can extend its prototype to also hold other bits of data and/or custom functionality.

 

An example of how this all works can be found here:

 

http://www.javascriptkit.com/javatutors/proto.shtml

 

You should then pass the function object into the db.transaction() 

Tim Neil
Director, Application Platform & Tools Product Management
Follow me on Twitter
Regular Contributor
Posts: 65
Registered: ‎01-21-2011
My Device: Not Specified

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

Hi,

 

Sorry I'm not quite following what you mean. 

 

 

 

Regular Contributor
Posts: 65
Registered: ‎01-21-2011
My Device: Not Specified

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

Do you mean something like the following? if so how i access the state in the guts of the transaction

 

 

var handler = function(tx){
    //how do i access myState in here??
}

handler.prototype.myState = "Some State";

db.transaction(handler);
Retired
Posts: 3,708
Registered: ‎10-16-2008
My Device: Z10
My Carrier: Rogers

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

I believe you would do it like the following... I'm also consulting with someone internally who has much more experience with this than I do to provide a concrete example.  I can't remember exactly if you can access it directly off of "this" or if it needs to be "this.prototype".

 

var handler = function(tx){
    alert(this.myState + ' or maybe ' + this.prototype.myState);
}

handler.prototype.myState = "Some State";

db.transaction(handler);
Tim Neil
Director, Application Platform & Tools Product Management
Follow me on Twitter
Retired
Posts: 2
Registered: ‎03-31-2011
My Device: Torch, PlayBook
My Carrier: AT&T

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

I think you are butting against a common error where you feel that the scope is captured at the time the function is instanciated. For example:

 

function MyFunc()

 {

   for (var i = 0; i < 10; ++i)

    {

      setTimeout(function()

                   { document.getElementById("SOME_DIV").innerHTML+=i+"<\BR>"; }

                 ,0);

    }

 }

 

You may expect to see the named DIV filled with 0<BR>1<BR>2<BR>... But it doesn't happen that way in proper JavaScript. You may see some values multiple times, or other values completely missing. What a lambda captures is only a pointer to the parent scope, so when the function executes, it does so with whatever the current value of "i" is at that time (often, you'll get the value 10), not the value it had when the function was created.

 

One technique is to capture the stuff you want in a local variable that is unique to each function you launch. For example, the code above would be rewritten as:

 

   for (var i = 0; i < 10; ++i)

    {

      var local_i = i;

      setTimeout(function()

                   { document.getElementById("SOME_DIV").innerHTML+=local_i+"<\BR>"; }

                 ,0);

    }

 

In this case, there is effectively a different local_i for each instance of the function when it executes because the scope you are binding to is local to each loop iteration. Also, in JavaScript, copies are by value. So this works for larger objects with more complex states.

 

In addition, you may want to lay off the lambdas a bit as in this case (inside a loop), you are creating a lot of memory activity. You can create a separate function (which will be parsed and compiled only once), and delegate to it from a simple lambda. I would do the following

 

function MyFunc()

 {

   for (var i = 0; i < 10; ++i)

    {

      var local_i = i;

      setTimeout(function() { MyPrinter(local_i); },0);

    }

 }

function MyPrinter(i)

 {

   document.getElementById("SOME_DIV").innerHTML+=i+"<\BR>";

 } 

 

Your mileage may varry, but i have seen the second pattern perform faster in most cases.

 

I hope i understood your problem properly and that this works for you.

 

PS: i know my use of innerHTML is evil here, but this is just an example and doesn't matter with the issue at hand.

 

Regular Contributor
Posts: 65
Registered: ‎01-21-2011
My Device: Not Specified

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

Hi,

 

Thanks for your reply Smiley Happy

 

With the first MyFunc you get 10,10,10,10,10,10,10,10,10,10 as you said but on the second one which is supposed to do it properly you get 9,9,9,9,9,9,9,9,9,9 and not 01,2,3,4,5,6,7,8,9, 

 

Smiley Sad

Developer
Posts: 189
Registered: ‎08-13-2008
My Device: Not Specified

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

Hi

 

Check out this post on for loops in transactions for async callbacks. It may help you out. 

 

for loops

 

Cheers

 

Andrew

Retired
Posts: 2
Registered: ‎03-31-2011
My Device: Torch, PlayBook
My Carrier: AT&T

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

I'll be damned... All my apologies... I forgot that through optimizations, i guess that innerscope is getting optimized and not re-created for each loop, causing the problem you observed.

 

However, the core technique is right in that what you need is to create a local and unique version of the data you care about to be captured correctly. You can see a better technique in the example abarber pointed to: basically, local anonymous function calls.

 

<DIV id="SOME_DIV"></DIV>
<SCRIPT>
for (var i = 0; i < 10; ++i)
{
(function(local_i){
setTimeout(function() { document.getElementById("SOME_DIV").innerHTML+=local_i+"<\BR>"; }, 0);
})(i);

}
</SCRIPT>

 Or, simplified:

 

 

<DIV id="SOME_DIV"></DIV>
<SCRIPT>
  function MyFunc(i)
    {
      setTimeout(function() { document.getElementById("SOME_DIV").innerHTML+=i+"<\BR>"; }, 0)
    }

  for (var i = 0; i < 10; ++i)
    {
      MyFunc(i);
    }
</SCRIPT>

 

You are basically making a real copy of your variables as part of a function call, creating a new local scope, and then doing the asych stuff.

 

The core technique is: make a copy of the data that is just for the instance of the async function you create (create a local unique scope environment effectively). If you don't that function only gets a pointer to the local scope, which is completely dynamic, and as i found out today (thanks for that), also applies to scopes for inner loops which seem to be optimized into a single actual scope during execution, which doesn't solve our issue.


 

 

 

Regular Contributor
Posts: 65
Registered: ‎01-21-2011
My Device: Not Specified

Re: html5_init.js creates different DB behaviour on OS5 and OS6!!! HELP!!!

THANKS SOOOOOOOOOOO MUCH guys... that was really starting to do my noggin in