XML-Based Searchable Database (part 3):
list matches, preloader and more...

By Joachim Schnier

Introduction

This tutorial is part 3 of the tutorial series about creating a simple database with XML and integrating it into Flash. This part shows you how to do advanced search using more complex XML files. Since I am not going into details about XML itself or how to access the XML with actionscript, I strongly recommend that you read first the other two tutorials A simple XML-based searchable Database and Part 2 of it.

What is new in this version 3 and what will you learn?

        
  • So far we were able to find only one match, but the reality is that often we have more than one match and want to see all matches listed. This is done using the listbox component
        
  • In the other tutorials I have shown how to use an input textfield to search for something. In this version I use radiobuttons and checkboxes, which will lead the user specifically to what he/she wants to find.
        
  • Finally, in this version I used a lot of components but components are always loaded prior to anything else. Here I show a preloader version, where the actual movie is loaded into a base movie.

    But first as usual have a look at the final product...

Download files

The SWF

    For some special features check the checkbox foreign. You will see text with links. To see the XML file click here. I am not sure the link works. In that case open the file after downloading.


The Stage of the Search Movie

    I will deal with the preloader later. First I will introduce the main movie. The stage is just an ordinary stage with a listbox, several textfields, radiobuttons and a checkbox. There is also an empty movieclip for pictures or movies, which can be selected from links in the textfield. The background for the main movie is a colored box.

Note: It is important to make the background a movieclip. Otherwise it will be transparent and the background will be the base movie.

    For the radiobuttons we have to fill up the form in the property inspector as shown in the picture. Even we have scripts for the buttons to set labels, we have to do that because I found it is important when the movie is a level1 movie within a base movie. The same is also true for the checkboxes.

property inspector

    We now group the radiobuttons. Normally they are by default grouped in one group. Since we have different options we regroup them, for example group1 is differentiating the age of the models and group2 the haircolor. We set one of the properties of each group true. Checkboxes cannot be grouped and are used to modify the search and make it even narrower. They will always be used in addition to radiobuttons here.

The Actionscript: part 1

Defining buttons and uploading XML files

    At the beginning we have a function, which will load a movie into an empty movieclip called movieHolder. This function is executed when a link in the textfield is clicked. We also define a similar function to go to another frame in our movie. We come back to that later. Finally we want to have a background picture in our base movie (_level0). When we use _root here we refer to _level0 and not to our uploaded movie.

//asfunction to load pics from the text files (see text files).
function loadPics(movie){
    movieHolder.loadMovie(movie);
}
//asfunction to go to a different frame from text files (see kim.txt)
function goFrame(frame){
	gotoAndPlay(frame);
}

//we control loading a background pic into the basemovie and reduce its alpha value.
_root.modelHolder.loadMovie("model_back.jpg");
_root.modelHolder._alpha = 50;

    Then we label the radiobuttons as shown here.

//these are radiobuttons of group1, where we define the label
over_30.setLabel("age over 30");
under_30.setLabel("age under 30");

//these are radiobuttons of group2, where we define the label
blond.setLabel("blond");
black.setLabel("black");

    Next we define some properties of the main textfield and the listbox such as size and background color.

//here we set the properties of the main textfield and the listbox
InstanceName_0.backgroundColor = 0xFEFFD9;
myList.setStyleProperty("shadow", 0x000000); 
myList.setSize(150, 100);
myList.setStyleProperty("background", 0xFEFFD9); 

    Now we are ready to define the main function for the submit button. The first three lines are to clear up the main textfield, the listbox and the picture holder whenever we perform a new search. The following lines determine which XML file will be uploaded. The user can determine this by choosing a radiobutton. We had split up the radiobuttons into two groups. The choice here is addressing group1 buttons. The Value of each button is the label which we gave the button.

//this is the main function for the "submit" button
function searchSite() {
    movieHolder.unloadMovie();//the movieholder for jpgs is unloaded every time
    myList.removeAll();//the listbox is made empty
    InstanceName_0.text = "";//the textfield is cleared
    //we now ask which XML file we want to upload using radiobuttons of group1
    if (group1.getValue() == "age under 30") {//the value is equal to the label here
        findModelsUnder30();
    } else if (group1.getValue() == "age over 30") {
        findModelsOver30();					
    }
}

    The XML uploading is executed here and will not be further discussed.

//uploading XML files, for details see earlier tutorials
function findModelsUnder30(){
    models = new XML();
    models.load("models1b.XML");
    models.ignoreWhite = true;
    models.onLoad = findModels;
    findModels();
}

function findModelsOver30(){
    models = new XML();
    models.load("models1.XML");
    models.ignoreWhite = true;
    models.onLoad = findModels;
    findModels(); 
}

The Actionscript: part 2

Accessing the XML tree

    We are now accessing the XML tree. The first part I will not further explain. You have to read previous tutorials for that.

//here we define our basic functions, which are used to parse different XML files
//for details see earlier tutorials
function findModels() {
    for (var count01=0; count01<= models.childNodes.length; count01++) {
        if (this.childNodes[count01].nodeName.toLowerCase() == "models") {
            var myModel01 = this.childNodes[count01];				
        }
    }
    findFemaleModels(myModel01);
}
//accessing childnodes
function findFemaleModels(femaleModel) {
    for (var count02=0; count02<=femaleModel.childNodes.length; count02++) {
        if (femaleModel.childNodes[count02].nodeName.toLowerCase() == "female") {
            var myModel02 = femaleModel.childNodes[count02];
        }
    }findHairColor(myModel02);
}

    At this point we go further into detail with our search. When you look at the XML file you will see that there are childnodes determining the haircolor, which is given as the attribute color of those nodes and is either blond or black. So first in our script we determine the variables we need to execute the script further, which are myModel03, which contains all childnodes downstream of the haircolor nodes and colorHair, which is the attribute of the haircolor nodes. As above we now get the values of the radiobuttons (group2) for the haircolor, whichever the user has chosen. However, we add one more option (we could add many more, which makes this search engine so powerful), which is a checkbox where the user can search for models who speak a foreign language. We, therefore, ask if checked is true or false and execute the corresponding functions, which are the same no matter which haircolor has been selected.

//accessing more childnodes
function findHairColor(coloured) {
    for (var count03=0; count03<=coloured.childNodes.length; count03++) {
        if (coloured.childNodes[count03].nodeName.toLowerCase() == "haircolor") {
            //we create variables, which we use later
            var myModel03 = coloured.childNodes[count03];
            var colorHair = myModel03.attributes.hair;
            //here we split our search using radiobuttons belonging to group 2.
            if (group2.getValue() == "blond") {//the value			
                if(colorhair == "blond"){
                    if (check1.getValue() == true){//we go further and check if checkbox was selected
                        findSubgroup_1 (myModel03);							
                    }else if (check1.getValue() == false){
                        findSubgroup_2 (myModel03);
                    }
                }//we do the same for the black haircolor, which we did for blond
            } else if (group2.getValue() == "black") {
                if(colorhair == "black"){
                    if (check1.getValue() == true){											
                        findSubgroup_1 (myModel03);							
                    }else if (check1.getValue() == false){
                        findSubgroup_2 (myModel03);
                    }
                }
            }
        }
    }
}

The Actionscript: part 3

Text in the textbox

    You probably wonder why I make a fuss about loading text in the textbox but this is pretty important here and I have spent hours trying to find different solutions. When our search was successful and we select one of the search items for display, we can only get one item displayed, since the listbox script allows only that: the item name and a property belonging to the item, which could be a text, url or something like that. But we want to be able to display many items, which we do by creating variables. This was no problem, when our search always resulted in one match as described in my previous tutorials. However, now we have to find a different solution. It is actually possible to do that but the script becomes really bulky. I have an example on my web site, when you go to flashspot and click on Advanced XML search engine. I do not go further into details. You can find out by yourself, but this version sets limits because of the bulkyness of the script.

    Why is this a problem? When you go through the childnodes of an XML file using a loop and ask for the childnodes creating a variable, the Flash XML parser will only list the last found node in a textfield for example. When you do a trace action, all nodes will be listed, but we cannot use a trace action for our purpose as you can imagine. So the solution I came up with is to create a separate textfile, which contains what we want: links to other websites and links to go to other frames in our movie and links to upload more movies or pictures. I first did not know it was possible but learnt it in the flashkit discussion forum. Now after all these explanations you know why we create a loadvars function to upload text files. I am not going to explain this further.

//function to load the text for the main textfield.
//"modeldescription" equals the attribute "ID", which holds the name for the text.
function loadText(modelDescription) {
    loadVarsText = new loadVars();
    loadVarsText.load(modelDescription);
    loadVarsText.onLoad = function(success) {
        if(success) {
            InstanceName_0.html = true;//we want to use html in the text.
            InstanceName_0.htmlText = this.myText;//"myText" is var name in the text document.
        }else{
            trace("not loaded");
        }
    }
}

The Actionscript: part 4

Completing our search

    We have now scripts, which will do the final job to complete our search. In this example we execute the function findSubgroup_1 (subGroup), which searches for blond or black models who speak a foreign language. After looping through the haircolor childnodes we ask by an if statement if count04>=0. This makes sure we include all nodes. Counting nodes in flash starts with 0, which we have to include as well. Then we ask if there are any nodes with the attribute "foreign", since these are the ones we are looking for disregarding others. Finally we want to make sure that there are no undefined nodes.

    If there was success and there were nodes then we want to know the names and descriptions, which are held by the var modelName and modelDescription. Since our description of the model can be in a textfile or in the XML file itself as the first child of a node, we have to distinguish that by another if statement. Finally we want to show a list of the matches, which is done by the function for the listbox chooseModel(modelName,modelDescription);, which is explained below. We write actionscript such as this one for all the search categories. You can see another example following this one.

//now we have to access all the childnodes,
//here for those which contain the attribute name "foreign"
function findSubgroup_1 (subGroup) {
    for (var count04=0; count04 <= subGroup.childNodes.length; count04++) {
        myModel04 = subGroup.childNodes[count04];
        //"count04" will count all childnodes starting from 0.
        //only the nodes with a positive match will be listed.	
        if(count04>=0 && myModel04.attributes.language == "foreign" && myModel04 != undefined){
            modelName = myModel04.attributes.name;//name attribute
            //the var modelDescription holds the nodeValue of the firstChild of each node
            modelDescription=myModel04.firstChild.nodeValue;
            //but when there is no nodeValue and therefore firstChild is undefined, then
            //this means we have an attribute "ID" and modelDescription will hold the
            //attribute ID, which is a text document.
            if (modelDescription == undefined){
                modelDescription=myModel04.attributes.ID;
            }//here we now execute the function for the listbox with the
            //two arguments "modelName", which holds the name of the models
            //and "modelDescription", which holds a nodeValue or the ID attribute.
            chooseModel(modelName,modelDescription);										
        }
    }
}

//this is essentially the same as in the previous chapter, except we will list
//all childnodes of a particular parent node.
function findSubgroup_2 (subGroup) {
    for (var count04=0; count04 <= subGroup.childNodes.length; count04++) {
        myModel04 = subGroup.childNodes[count04];
        if(count04>=0 && myModel04 != undefined){
            modelName = myModel04.attributes.name;
            modelDescription=myModel04.firstChild.nodeValue;		
            chooseModel(modelName,modelDescription);
        }
    }
}

The Actionscript: part 5

Listbox functions

    We now have to create listbox functions. The first function is the changehandler for the listbox called chooseMenu(myList). First we clear our movieholder, then we define the Value of the listbox, which is the var modelDescription. If that is not undefined, then we either upload a textfile with the description or if we access the description as the first child in our XML file.

//listbox changeHandler function
function chooseMenu(myList) {
    movieHolder.unloadMovie();//we clear the movieholder, every time a listbox
    //item is selected. "modelDescription" is the value for each item.
    modelDescription = myList.getValue();
    if (myList.getValue()!=undefined){
        //we either execute the loadVars function(see above) or show the nodeValue
        //in the main textfield "InstanceName_0.text" depending on what
        //modelDescription will hold. When you execute this with "Test Movie", 
        //there will be a complain, because either hasn't been found but that does not
        //matter for execution of the movie itself.
        loadText(modelDescription);
        InstanceName_0.text = modelDescription;
    }
}

    The actual function combining the list and changehandler is chooseModel(model,description). We define here by myList.addItem() that to each modelName model there will be a modelDescription description. Then we set the changehandler function, which we have defined above so that whenever the user clicks on an item (model) the corresponding whatever it is (description) is shown. This can be a url, movie, picture, anything which we define.

//function for the listbox
function chooseModel(model,description){
    //this script defines the item "model", which is "modelName" and its
    //value description, which is "modelDescription". All items will be listed.
    myList.addItem(model,description);
    //this will execute the above function and only one item be selected.
    chooseMenu(myList);
    myList.setChangeHandler("chooseMenu");
}

Textfiles

    If you open one of those textfiles you will see some url links: <A HREF="asfunction:loadPics,kim.jpg"><U>Kim's Picture</U></A> or <A HREF="asfunction:goFrame,2"><U>Web site</U></A> for example. These functions are executed on the main timeline in our movie and as you may remember we have defined them right at the beginning. And this is the trick I told you to get as much out of the var modelDescription as we can. By creating this textfield we can add more variables.

Preloader

    Finally, I would like to present a preloader, which I made especially for files with components but of course can be used for anything. Components are loaded prior to anything else. If you use the usual preloader and your file has many components you would notice that for sometimes your preloader does not appear untill all the components are loaded. If the separate the preloader from the actual movie, this does not happen.

    This is the script in frame 1.

stop();
//check the "preloader" MC for a script.
//we make the loaderbar at the beginning invisible,
//because it will show up before the movie is loaded.
_root.loaderBar._visible = false;
_root.preHolder.loadMovie("advanced_search.swf");
We make our loaderBar invisible, because otherwise it will show up and start, even no loading occurs and then when loading it will start again. Inside of the empty movieclip preHolder we have the following code:
onClipEvent(enterFrame){
    if(this._url != _root._url && !this._url.loaded) {		
        var kilobytes = Math.ceil(this.getBytesTotal()/1024);
        _root.frame = Math.ceil((this.getBytesLoaded()/this.getBytesTotal())*100);
        _root.loaderBar.gotoAndPlay (_root.frame);
        _root.frame = "loading " + _root.frame + "% of " + kilobytes + "kb";
        _root.loaderBar._visible = true;
        if(this.getBytesLoaded() == this.getBytesTotal()){
            this._url.loaded = true;			 
            _root.gotoAndStop("start");
        }
    }
}
This is one of those typical loaderscripts, which I mixed together from other scripts, but it works and that is important. Important also that here we make the loaderbar visible again for loading. Finally we have a second frame with this code:
stop();
And that is it, ready to go.

Conclusion

    You can now add a search input textbox to this search engine. You can put additional buttons on the uploaded movie to close it and on the base movie to upload other movies.

Note: Always make sure the pathes are correct and you refer to the base or uploaded movie.

    Hopefully you have enjoyed this tutorial. If you have any question or suggestion e-mail me and one more thing: Don´t forget to rate this tutorial.