Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

Using addEventListener & fireEvent

Hi, I'm one day in to Appcelerator and could do with some basic help please…

I have a bit of code that is currently triggered using addEventListener with a picker. I want the same code to be triggered using addEventListener for two other pickers, i.e. changing all three pickers has the same effect.

Is there a way for one addEventListener to point to the other, perhaps using fireEvent? - I want to avoid having to duplicate my code for each individual picker event.

Hope that makes sense!

— asked October 24th 2011 by Ian Taylor
  • addeventlistener
  • fireevent
0 Comments

4 Answers

  • Yes, create an app level event listener that contains the code that is common for your app. Then, within the eventlistener for each picker, fire that event with the necessary data to your custom event listener.

    Ti.App.addEventListener('custom_event', function(e) {
      // Common app code
    });
    
    // You picker listener
    somepicker.addEventListener('change', function(e) {
      Ti.App.fireEvent('custom_event', {custom_data:e});
    });
    
    — answered October 24th 2011 by Anthony Decena
    permalink
    0 Comments
  • Although app-level events solve some issues related with the usage of common code, they introduce other problems. If not used carefully, they can create memory leaks, here is simple example:

    Ti.App.addEventListener('update_label', function(e) {
        label1.text = e.text;
    });
    

    As you can see, label's reference is present in app-level event listener function. That listener will never be released (because it's app-level) and therefore, label will not be released from memory.

    And what about function generalization? What if you want to make it work for any label? Well, you could pass label's reference via fire event function and you would even solve memory issue, but then you would have a (OOP) design problem since that functionality is related to all labels and not in general for all elements - therefore, it should be present in all label instances (well, if you want to reuse it).

    Now, I'm not sure have I understood your problem well, but it seems to me that you want to add same event listener to every label. You can do that in several way, one simple is to create non-anonymous function and add it to all labels as event listener:

    var onClick = function(e) {
        Ti.API.debug(this); //should be label instance
    };
    
    label1.addEventListener('click', onClick);
    label2.addEventListener('click', onClick);
    label3.addEventListener('click', onClick);
    

    Even better - you can create function factory that creates labels with particular functionality:

    function LabelWithClick(options) {
        var label = Ti.UI.createLabel(options);
        label3.addEventListener('click', function(e) {
            //do something
        });
        return label;
    }
    

    If you want this function to be globally available, create global namespace object. Let me know if you need help with that, or if you're using multi-context windows.

    The advantage of this approach is - the label created with this way will be garbage collected and it's event listener also. Now you have functionality related to the labels at right place and you could also separate your code:

    function MyLabel(options) {
        var label = Ti.UI.createLabel(options);
        return label;
    }
    
    function MyLabelWithClick(label) {
        label3.addEventListener('click', function(e) {
            //do something
        });
        return label;
    }
    

    Put that two functions in separate files and you have basic VC approach (MVC without a model :D )

    If I missed the point, please correct me. :)

    — answered October 25th 2011 by Ivan Škugor
    permalink
    3 Comments
    • I have some copy-pasting issues in my answer, "label3.addEventListener" should be "label.addEventListener" of course! :)

      — commented October 25th 2011 by Ivan Škugor
    • Thanks for the very detailed explanations - maybe a little too detailed for me! I'm not interested in passing variables around my whole app, so would like to do this without app-level events at this stage.

      I don't know if it's the done thing, but I'm going to take the liberty of copying my code below. The code works fine at the moment, but what I'd like to do is to get the same functionality for the other two pickers, whilst avoiding duplicating code unnecessarily.

      I am only a few days into Titanium (and in fact any code for that!) so hopefully you'll excuse me for asking what is probably a really easy question.

      var win = Titanium.UI.currentWindow;
      
      // build custom tableView data/layout
      var array = [];
      
      var letterRow = Titanium.UI.createTableViewRow({height:46, className:'letterRow'}); 
      var numberRow = Titanium.UI.createTableViewRow({height:46, className:'numberRow'}); 
      var comboRow = Titanium.UI.createTableViewRow({height:46, className:'comboRow'}); 
      
      var letterLabel = Ti.UI.createLabel({color:'#000000', text:"Letter", font:{fontSize:17, fontWeight:'bold'}, top:8, left:12, height:24, width:170});
      var letterValue = Ti.UI.createLabel({color:'#3D4460', text:"", font:{fontSize:17, fontWeight:'normal'}, top:11, left:102, height:20, width:180, textAlign:'right'});    
      var numberLabel = Ti.UI.createLabel({color:'#000000', text:"Number", font:{fontSize:17, fontWeight:'bold'}, top:8, left:12, height:24, width:170});
      var numberValue = Ti.UI.createLabel({color:'#3D4460', text:"", font:{fontSize:17, fontWeight:'normal'}, top:11, left:102, height:20, width:180, textAlign:'right'});    
      var comboLabel = Ti.UI.createLabel({color:'#000000', text:"Combo", font:{fontSize:17, fontWeight:'bold'}, top:8, left:12, height:24, width:170});
      var comboValue = Ti.UI.createLabel({color:'#3D4460', text:"", font:{fontSize:17, fontWeight:'normal'}, top:11, left:102, height:20, width:180, textAlign:'right'});    
      
      letterRow.add(letterLabel);
      letterRow.add(letterValue);
      numberRow.add(numberLabel);
      numberRow.add(numberValue);
      comboRow.add(comboLabel);
      comboRow.add(comboValue);
      
      array.push(letterRow);
      array.push(numberRow);
      array.push(comboRow);
      
      // view initialisation
      var tableView = Titanium.UI.createTableView({data:array, style:Titanium.UI.iPhone.TableViewStyle.GROUPED});
      var letterPickerView = Titanium.UI.createView({height:248,bottom:-248});
      var numberPickerView = Titanium.UI.createView({height:248,bottom:-248});
      var comboPickerView = Titanium.UI.createView({height:248,bottom:-248});
      
      
      // letter picker initialisation
      var letterPicker = Titanium.UI.createPicker({top:0});
      letterPicker.selectionIndicator=true;
      var letterPickerValues = [
          Titanium.UI.createPickerRow({title:'A',value:'1'}),
          Titanium.UI.createPickerRow({title:'B',value:'2'}),
          Titanium.UI.createPickerRow({title:'C',value:'3'}),
      ];
      letterPicker.add(letterPickerValues);
      letterPickerView.add(letterPicker);
      
      // number picker initialisation
      var numberPicker = Titanium.UI.createPicker({top:0});
      numberPicker.selectionIndicator=true;
      var numberPickerValues = [
          Titanium.UI.createPickerRow({title:'1',value:'1'}),
          Titanium.UI.createPickerRow({title:'2',value:'2'}),
          Titanium.UI.createPickerRow({title:'3',value:'3'}),        
      ];
      numberPicker.add(numberPickerValues);
      numberPickerView.add(numberPicker);
      
      // combo picker initialisation
      var comboPicker = Titanium.UI.createPicker({top:0});
      var col1 = Ti.UI.createPickerColumn({opacity:0});
      var col2 = Ti.UI.createPickerColumn();
      comboPicker.selectionIndicator=true;
      var col1Values = [
          col1.addRow(Titanium.UI.createPickerRow({title:'A',value:'1'})),
          col1.addRow(Titanium.UI.createPickerRow({title:'B',value:'2'})),
          col1.addRow(Titanium.UI.createPickerRow({title:'C',value:'3'})),
      ];
      var col2Values = [
          col2.addRow(Titanium.UI.createPickerRow({title:'1',value:'1'})),
          col2.addRow(Titanium.UI.createPickerRow({title:'2',value:'2'})),
          col2.addRow(Titanium.UI.createPickerRow({title:'3',value:'3'})),
      ];
      comboPicker.add([col1,col2]);
      comboPickerView.add(comboPicker);
      
      // picker animations
      var slideIn =  Titanium.UI.createAnimation({bottom:-43});
      var slideOut =  Titanium.UI.createAnimation({bottom:-251});
      
      // event functions
      tableView.addEventListener('click', function(eventObject){
          if (eventObject.rowData.className == "letterRow")
          {
              comboPickerView.animate(slideOut);
              numberPickerView.animate(slideOut);    
              letterPickerView.animate(slideIn);        
          }
          else if (eventObject.rowData.className == "numberRow")
          {
              letterPickerView.animate(slideOut);
              comboPickerView.animate(slideOut);
              numberPickerView.animate(slideIn);    
          }
      
          else if (eventObject.rowData.className == "comboRow")
          {
              letterPickerView.animate(slideOut);
              numberPickerView.animate(slideOut);    
              comboPickerView.animate(slideIn);
          };
      });
      
      var letter;
      var number;
      var combo;
      
      //combo picker selection
      comboPicker.addEventListener('change',function(e)
      {
          letter = comboPicker.getSelectedRow(0).title;
          letterValue.text = letter;
          letterPicker.setSelectedRow(0,letter-1);
      
          number = comboPicker.getSelectedRow(1).title;
          numberValue.text = number;
          numberPicker.setSelectedRow(0,number-1);
      
          combo = letter + ":" + number;
          comboValue.text = combo;
      
          tableView.setData(array);
      });
      
      // build display
      win.add(tableView);
      win.add(letterPickerView);
      win.add(numberPickerView);
      win.add(comboPickerView);
      

      — commented October 26th 2011 by Ian Taylor
    • As I said and shown, you can simply write a function that constructs pickers (like everything else that you want to make reusable).

      Don't use row's property "className" for things other than the one it was intended to be used. "className" property is used to group rows with same layouts, so if you have rows with pickers, you should use same class name for them. If you need to identify particular row, use some other property (id, or some custom name for property).

      — commented October 27th 2011 by Ivan Škugor
  • Thanks for the quick answer. I'm not sure how/where to implement the app level EventListener, presumably in app.js ? But is this a problem if the various variables in the common app code are defined in a different .js file?

    I tried putting the app level code at the head of the .js file that contains my common app code, but it didn't work.

    I'll keep experimenting over the next couple of days.

    — answered October 24th 2011 by Ian Taylor
    permalink
    0 Comments
  • Try not to answer you own questions unless you are providing an answer. Use the comment system when responding to a posted answer.

    It doesn't matter where you create your app level events because you are attaching the event to Ti.App, so anywhere that has access to Ti.App will have access to your event listener.

    Check the custom events section of the Kitchen Sink demo for more on implementing them in your code.

    — answered October 24th 2011 by Anthony Decena
    permalink
    0 Comments
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.