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
Developer
Posts: 112
Registered: ‎06-14-2010
My Device: Passport
My Carrier: Virgin Mobile Canada

BBUI.js / BB10 / WebWorks help needed re: turning SQLite query result into markup and adding UI elements to screen in "onscreenready"

[ Edited ]

Yes, this is a "long read" but if anyone is daring enough to respond in detail, perhaps it will serve as a cautionary tale to other newbies like me who are used to standard "Web site" programming but lack "AJAX" style programming skills needed for WebWorks app development.

I'm working on a BBUI.js / BB10 app that uses an SQLite database. I'm not using jQuery (and would rather not undertake that learning curve right now)

I'm having difficulties with how the "onscreenready" callbacks work, and it's probably got something to do with my lack of understanding of variable scope and how I'm using callback functions. Also, please forgive my cringe-worthy abuse of programming terminology, but I'm just a guy with a logical mind, not a guy who memorizes stuff very well.

Some premises I'm working with.

1) If I have a .js file linked in to my "index.html" (main screen) file, those functions and any variables declared outside of functions are available to me in any script, even one that's in a <script> tag in a "screen" HTML <div> fragment. If that's not right, then that's probably a huge part of my problem.

2) if i declare a variable OUTSIDE a function, then change its value INSIDE a function, I have access to that variable's changed value OUTSIDE the function.

3) if I declare a variable INSIDE a function and set its value, its value is only available if I "return" that value from the function, and the value would be assigned to a variable by way of a call like "var myVar = myFunction();"

Ok, I'm pretty sure that's all correct, if perhaps oversimplified.

...but then we get into the multiple-layers of using BBUI.js "onscreenready" for all the different screens in my app and then trying to use general-purpose database functions within "screens" for getting values from tables that are pertinent to that screen (where making these screen-specific database functions "global" in scope would be inefficient for one reason or another). I do a lot of "alert" statements in my code just to see that objects are not "undefined" or "null" etc. and a lot of times, they are "null" when I don't think they should be, or I don't know how to not make them "null" even though I understand why they are.

Note: BBUI.js works great, my objects are styled exactly as they should be, the app (now, after some struggle) runs the same in Ripple Emulator as it does in the 10.2x Simulator.

I have my index.html file as the "main" page declared in my config.xml. in the <head> of that main page I have included a few general purpose .css and .js files, including those that support bbui.js and my custom.js files.

The bb.init has "onscreenready: function(element, id, params) {...}" and "ondomready: function(element, id, params) {...}" "event handlers"(?) and calls the "initApp()" function which is in my custom.js file.

"initApp()" first checks if this is the apps first run: I am using localStorage for that, setting a simple flag. Perhaps I need to do something more? At any rate, that seems to work OK.

The first thing the first-run setup tries to do is check if the databases exist. Using "window.openDatabase" creates the tables and populates them if the tables don't exist (calling a .SQL file via XMLHttpRequest and separating many lines of individual "CREATE" statements in a single file into separate SQL statements by splitting a variable at "\n" characters into individual array values... this works fine).

I know there is a "creation callback" (when window.openDatabase creates the non-existent database rather than opening an existing one) that could be used, but when I tried that I had... difficulties... but I then have a function (in my db_module.js file that's included in the "index.html" <head>) that just attempts to run a query against a given static known table (containing lookup values destined for a <select> dropdown) that is the last table created in my list of "CREATE" statements, so if that table exists and has values, then logically all the others must exist as well (assuming there's no way that a series of CREATE query statements (that aren't in and of themselves messed up) that worked and created the aforementioned table properly could be "interrupted" halfway through...although if someone launched the app for the first time then their battery died or they closed the app it could cause problems... so perhaps I need to account for that... at any rate...). I have examined the database in Chrome's developer tools and all the tables do exist and all the values are there. So, I am confident that the code that creates the databases and checks for their existence work fine.

Note: the database is opened in the initApp() function by a command "theDatabase = window.openDatabase(...)", so I should have an object called "theDatabase" that is an SQLite database object that I should be able to interact with freely throughout the app... right? I mean, I shouldn't have to pass "theDatabase" as an parameter to functions, should I? It's just always there, ready to use, right?

I have documents containing written descriptions of all the logic and screens for my app, and I have built empty .html "screen" files and created their linked .js files. In the .js files I have created functions that are mostly empty but have a lot of comments for example "// code that populates this form goes here" in the "onscreenready" function (my understanding is that I should generate HTML in "onscreenready" not "ondomready", more on that later). A handful of the screens are accessed by the Action Bar, and others are the result of a long press on an item in a list or on the Menu or on the Context Menu or on a "Add New Item" button on the Action Bar... all of that works and all the screens are loaded appropriately... I have even successfully implemented generating of the <option> elements in dropdown lists programmatically... but not from a database... which is why I need to sort this out).

I started to the task of writing the code to hit the database then process the result of a simple SELECT query into markup that could appear as user interface components, and after initial success (getting the data, turning it into "alerts" that list all the field names / values) I can't seem to get farther, turning that result into UI elements (like items in an image list) and having them render properly.

Every "screen" includes its own "<script>" tag that includes a screen-specific .js file.

Each .js file has an "___OnScreenReady" and "___OnDomReady" function (where ____ is the page name, like "item" or "location" etc) specific to that screen (at this point, most are empty and just throw an "alert" to state that they have been called from the "onscreenready" and "ondomready" event handlers in my "index.html" page)

In the "index.html" file, the bb.init "onscreenready" and "ondomready", um, methods(?) include a "switch / case" statement for every single screen in the app, and a "default" in case I create screens and don't provide a "case"... each "case" calls the related function within the screens .js file (so case: "items" calls "itemsOnScreenReady()" ). Those functions are called successfully (I know because each function has an alert that tells me the function has been called.)

I don't pass any arguments to these "___OnScreenReady" functions that are called by the bb.init "onscreenready:" event... maybe I should or must? I considered sending the "element" and "id" and "params" with each "___OnScreenReady()" function call in the "case" statement, but thought that was unnecessary in most cases, because these objects / variables were declared in such a way that they could just be used as is by functions throughout the app.

Oh, one other thing: I do use javascript to generate the "Menu" and the "Action Bar" for every screen... the same Action Bar appears on all screens that have an Action Bar, but then on some screens additional items are added as "buttons"... but the "action" items are always the same... and this all works: each screen is loaded, gets it's Action Bar, gets its particular extra buttons added, no problem... but all of the markup is generated from code, there is no database result involved.

 

Other screens are forms that would be displayed "modal-ly" like editing a item on a form, so they don't get Action Bars, they just get Context menus... and the Menu appears on all the screens EXCEPT the screens that are themselves shown when one of the main menu items is pressed... I use an "if" statement in the "onscreenready:" to either add or not add the "Action Bar" and "Menu" to a screen based on the screen's "id"... and I also use a "switch" statement within the markup generator to set the Action Bar item that loads the current screen to "active" and disable its "pushScreen" command... that all works fine. I haven't even started to try to get the current form data to persist between screens, but that'll just be a JSON object that is stored and managed and restored when the screen is loaded again from a "back" button... oh, I do keep track of which screen called the current screen so there's that... anyway...

It's just getting the results of database queries to show up as controls on my screens that has me running around in circles.

Let's say I have a screen called "Items" that is loaded by bb.pushScreen "items.html" and "items". Some objects, like "pill buttons" are hard coded in the HTML of the file, because they're unique to that file so why generate them by script? Others, like the Action Bar and the Menu are generated by scripts in my "custom.js" and then inserted into the file in the main "onscreenready:" event, using the "id" of the screen being 'pushed' to configure same.

In my "itemsOnScreenReady()" function that's in the "itemsScreen.js" file, I simply do a direct database transaction:

theDatabase.transaction(
        function (theTransaction) {
            theTransaction.executeSql("SELECT * FROM Items ORDER BY ItemName ASC;", [], itemsQueryHandler, itemsErrorHandler);
        }
    );

 

Crude? perhaps, but it works and I JUST WANT MY DATA DAMMIT I DON'T WANT TO WRITE AN ENTIRE GOSH DARN LIBRARY TO HANDLE A SIMPLE QUERY. That notion might be totally in and of itself wrongheaded, and even the root cause of my difficulties, but really, I have been trying to keep this very simple so I can understand what's going on, rather than just copy example code that I can't really change or maintain... after I understand how it works, I will find opportunities to consolidate code into libraries and make the code more efficient both in terms of maintenance and performance, but on a 1.5 gigahertz dual core processor, will this HAVE to be much more efficient? It ain't "folding at home" after all. Anyway...

You will see "itemsQueryHandler" and "itemsErrorHandler". Of course, there are functions in that same .js file, articulated as:

function itemsQueryHandler(theTransaction, theResult) {
    var theItemList = document.createElement('div');
    //do the needful to turn the SQL query result into BBUI "image-list" markup with items and headers and stuff.
    alert('theItemList is a ' + theItemList);
    alert('theItemList markup is ' + theItemList.outerHTML);
}

 
...and the code in place of that "// do the needful" comment works, where I'm creating the individual Items and create a new header when the first letter of the item is different than the last, using appendChild to get them into the main List and it all comes out perfectly, with all the values from the database properly incorporated, as evidenced by the alerts... this is markup that jives with the "ImageList" syntax described on the BBUI.js GitHub documentation.

...but from there, it seems to fall on the floor.

in the same function, I attempt to put the "theItemList" into my "screen" thusly:

var theScreen = element.querySelector['data-bb-type=screen'];
screen.appendChild(theItemList);

 and get errors like (paraphrasing) "theScreen has no querySelector object".

I've tried putting that code in lots of places and sometimes "theScreen" isn't an object, or "theItemList" isn't an object or something and when "theItemList" does work its way into the code, (I examine the following object in the Google Chrome "resources"):

 

<html>
    <body dir="ltr">
        <section id="ui">
            <section class="middle">
                <section id="device-container"...>
                    <section id="viewport-container"...>
                        <iframe id="document" ...>
                            #document
                                <html>
                                    <body>
                                        <div id="hexstuff">
                                            <div id="thisIsMyScreen"> // had assigned the page I'm working on an id at one point, possibly in desperation)

 ...and in that last <div> are all the action bar objects and menu objects and the markup I hard wired into the "item.html" file and so on... but the whenever I've been able to make my BBUI "image-list" object show up in there, it was a sibling to my screen, not a child of it. Other controls in the "items.html" file are laid on top of it, and the code doesn't look like a BBUI list (even though that markup matches the specs perfectly).

So... somewhere... in there... I'm not able to properly "preserve" the markup generated in the "itemsQueryHandler" that generates the markup... or I'm not able to deliver it to the "appendChild" function properly.

 

I did try a to "impose" a image-list style on the "theItemsList" object using the "bb.imageList.style(theItemsList)" erm, is that a method? but that didn't have a desirable effect (and I don't think it's actually necessary, just another attempt on my part to make it work).

Is there a kind soul out there with the patience to read this and assess the nature of my difficulties, which can include "stupidity" and "ignorance"? If I'm in over my head, that would be good to know, but I've made it this far, and as far as learning more about object-oriented programming in javascript in particular, I would appreciate directions to a good tutorial. I'm on the cusp of a revelation, I'm sure, but at this point, I'm experimenting and the cause-and-effect chain has me shackled to my desk... I would like to put out this BB10-exclusive app (maybe in time to qualify for the Oct 21st deadline) as close to "built for BlackBerry" as can be, and certainly can't afford to pay a developer right now... so any and all help will be appreciated when I'm up there accepting my Oscar for best supporting actor (Yeah, I expect the success of my app to lead to other opportunities!)

Thanks in advance.

Developer
Posts: 112
Registered: ‎06-14-2010
My Device: Passport
My Carrier: Virgin Mobile Canada

Re: BBUI.js / BB10 / WebWorks help needed re: turning SQLite query result into markup and adding UI elements to screen in "onscreenready"

BUMP: I know it's lengthy, but while it may be a long read, I only hope someone can identify (not necessarily explain) what I might be doing wrong either conceptually or just in the order or manner I'm trying to do things. Once I have been pointed in the right direction, I will do more research on my own... but as it stands, there are too many variables for me to research and when I try to implement a change, and it still doesn't work, I don't know if I fixed the problem and something ELSE is wrong or if I'm on the wrong track.

 

I am still trying to get it working on my own, but would appreciate any pointers.

Contributor
Posts: 35
Registered: ‎09-15-2012
My Device: AT&T Torch 9810
My Carrier: AT&T

Re: BBUI.js / BB10 / WebWorks help needed re: turning SQLite query result into markup and adding UI elements to screen in "onscreenready"

[ Edited ]

What if you cache a reference to your screen element in the .transaction function and have a reference closure to it in the success query callback. Observe,

db.transaction( function(tx) {
 var screen = document.querySelector(..)
 tx.executeSql(..., [], itemQueryHandler(screen), <errorhandler> )

 function itemQueryHandler(screen) {
   return function(queryResult) {
   var imageList = document.createElement('div')
   //turn image list into a BBUI image list.
   
   screen.append( imageList )
  }
 }

}

 You can verify that 'screen' object is valid before the query is executed. Or better yet, you can cache the screen object out of the db.transaction(..) and using the "closure" method shown above, pass it to your itemQueryHandler.