0

I am trying to modify the following titanium Widget.

https://github.com/pablorr18/TiFlexiGrid

It is a photo gallery, where you can fetch remote images and store it in a gallery. See this question for a background to what the problem is:

Modifying widgets in an alloy project

The trouble I am having is that, like the poster in that thread said, I am unable to get the variable passed (in this case image URL) to my controller using callback functions. In Widget.JS, at the bottom of the file, I added the following code:

Widget.xml

<Alloy>

    <View id="fgMain">
        <Button title="Click me!" onTouchend="buttonClicked"/>
        <View id="fgWrapper">
            <ScrollView id="fgScrollView"/>
        </View> 
    </View>
</Alloy>

Widget.js

// This will hold our callback
var onClickCallback;

// The button has been clicked, call callback
function buttonClicked(e) {
    if(typeof(onClickCallback) === 'function') {
        onClickCallback({ type:'clicked!' }); }
   }


// Assign our callback
function onClick(callback) {
    onClickCallback = callback;
};

// Make the onClick function public
exports.onClick = onClick;

I was then hoping to go into my main app and get the values from the callback function like this:

photoGallery.xml

<Alloy>
    <Window>
        <Widget id="myWidget" src="myWidget" />
    </Window>
</Alloy>

photoGallery.js

// Now we can intercept the click within the widget
// and use the values passed
$.myWidget.onClick(function(e) {
    alert(e.type);
});

The trouble is, as the onTouchend event did not fire, I was unable to pass the variable to the controller which inherits the widget as the callback functions are not set.

The original widget.js code is as follows:

exports.createGrid = function(args){
    var params = args || {};
    //Ti.API.info('Params es ---> '+ JSON.stringify(params));

    var columns = params.columns || 4;
    var space = params.space || 5;
    var data = params.data || {};
    var options = params.params || {};
    var layout = params.layout || 'gallery';
    var screenWidth = params.width || Ti.Platform.displayCaps.getPlatformWidth();
    if (OS_ANDROID) {
        screenWidth /= Ti.Platform.displayCaps.logicalDensityFactor;
    }
    var newWidth = screenWidth - space;
    var columnWidth = (newWidth / columns) - space;
    var frameBGcolor = options.backgroundColor || '#fff';

    //ADJUST THE SCROLLVIEW
    $.fgScrollView.left = space;
    $.fgScrollView.top = space;
    $.fgScrollView.right = -1;

    $.fgMain.backgroundColor = frameBGcolor;

    for (var x=0;x < data.length; x++){

        var frame = Ti.UI.createView({
            width:columnWidth,
            height:columnWidth,
            backgroundColor:options.gridColor || '#eee',
            top:0,
            left:0,
            right:space,
            bottom:space
        });

        var overlay = Ti.UI.createView({
            width:Ti.UI.FILL,
            height:Ti.UI.FILL,
            backgroundColor:'transparent',
            zIndex:1,
            strImage:data[x].image
        });

        var gridElement;

        //TYPE OF LAYOUT
        switch(layout){
            case('gallery'):
                gridElement = Widget.createController('gallery',{
                    image:data[x].image,
                    title:data[x].title,
                    width:columnWidth,
                    padding:options.padding || 10,
                    showTitle:options.showTitle || false
                }).getView();
                overlay.addEventListener('click',function(e){
                    exports.openModal(e.source.strImage);
                });
                break;
            case('customView'):
                gridElement = data[x];
                break;
        }

        frame.add(gridElement);
        // This condition makes the overlay not be added if it's not gallery layout.
        // It's used to make the custom view, caputre the click method. If not,
        // The overlay is on top of it and captures the click.
        if(layout == 'gallery')
            frame.add(overlay);

        $.fgScrollView.add(frame);
    };
};

exports.openModal = function(url){
    var overlay = Ti.UI.createView({
        width:Ti.UI.FILL,
        height: Ti.UI.FILL,
        backgroundColor:'#000',
        opacity:0,
        zIndex:100
    });

    var topView = Ti.UI.createView({
        width:Ti.UI.FILL,
        height: Ti.UI.FILL,
        zIndex:1200,
        visible:false
    });

    //this gets image , adds it to top view
        var imgView = Ti.UI.createImageView({
            image: url,
            width:Ti.UI.SIZE,
            height: Ti.UI.SIZE
        });



    //add it
    topView.add(imgView);
    $.fgMain.add(overlay);

    if (OS_IOS){
        //ANIMATION OF OVERLAY
        overlay.animate({opacity:0.7,duration:200});

        //ANIMATION FOR POP EFFECT
        var t = Titanium.UI.create2DMatrix();
        t = t.scale(0);
        var a = Titanium.UI.createAnimation();
        a.transform = t;
        a.duration = 200;

        $.fgMain.add(topView);
        topView.animate(a);

        a.addEventListener('complete', function(){
            topView.visible = true;
            var t2 = Titanium.UI.create2DMatrix();
            t2 = t2.scale(1.2);
            topView.animate({transform:t2, duration:200},function(e){
                var t4 = Titanium.UI.create2DMatrix();
                t4 = t4.scale(1.0);
                topView.animate({transform:t4, duration:200});

                //alert('animation complete');
                //hide cancel button


            });
        });
    }
    else{
        //ANIMATION OF OVERLAY

        overlay.animate({opacity:0.7,duration:200},function(e){
            topView.visible = true;
            $.fgMain.add(topView);
        });

    }

    topView.addEventListener('click',function(e){
        if (OS_IOS){
            var t3 = Titanium.UI.create2DMatrix();
            t3 = t3.scale(1.2);
            var a2 = Titanium.UI.createAnimation();
            a2.transform = t3;
            a2.duration = 200;
            topView.animate(a2);


            a2.addEventListener('complete', function(){
                var t5 = Titanium.UI.create2DMatrix();
                t5 = t5.scale(0);
                topView.animate({transform:t5, duration:200},function(e){
                    $.fgMain.remove(topView);
                    overlay.animate({opacity:0,duration:200},function(e){
                        $.fgMain.remove(overlay);

                    });
                });
            });
        }
        else{
            $.fgMain.remove(topView);
            overlay.animate({opacity:0,duration:200},function(e){
                $.fgMain.remove(overlay);
            });
        }

    });

};

exports.clearGrid = function(){
    $.fgScrollView.removeAllChildren();
};

From looking at it, the only way it seems to get an event handler to register is to put it one of the functions (createGrid) and call it like this:

exports.createGrid in widget.xml

    var button = Titanium.UI.createButton({
        title : 'Use Picture',
        top : 10,
        width : 100,
        height : 50
    }); 

    button.addEventListener('click',function(e)
    {

   Titanium.API.info(url);
  //do not want to store in global variable
   Alloy.Globals.urlFromGal = url;

    });

//add it
    topView.add(imgView);
    topView.add(button);
    $.fgMain.add(overlay);

However I am not sure how to adapt that code to this:

// This will hold our callback
var onClickCallback;

// The button has been clicked, call callback
function buttonClicked(e) {
    if(typeof(onClickCallback) === 'function') {
        onClickCallback({ type:'clicked!' }); }
   }


// Assign our callback
function onClick(callback) {
    onClickCallback = callback;
};

// Make the onClick function public
exports.onClick = onClick;

So that I am able to access the url variable outside of the widget.js scope. I could use GLOBAL Variables - by that store the variable in one instead for this task, however I want to avoid doing things this way.

UPDATE:

Unable to get it working, but I have added the callback into an event handler. Got the event handler to fire, but not sure how to pass the callback data to onClick function:

var onClickCallback;

exports.createGrid = function(args){
    var params = args || {};
    //Ti.API.info('Params es ---> '+ JSON.stringify(params));

    var columns = params.columns || 4;
    var space = params.space || 5;
    var data = params.data || {};
    var options = params.params || {};
    var layout = params.layout || 'gallery';
    var screenWidth = params.width || Ti.Platform.displayCaps.getPlatformWidth();
    if (OS_ANDROID) {
        screenWidth /= Ti.Platform.displayCaps.logicalDensityFactor;
    }
    var newWidth = screenWidth - space;
    var columnWidth = (newWidth / columns) - space;
    var frameBGcolor = options.backgroundColor || '#fff';

    //ADJUST THE SCROLLVIEW
    $.fgScrollView.left = space;
    $.fgScrollView.top = space;
    $.fgScrollView.right = -1;

    $.fgMain.backgroundColor = frameBGcolor;

    for (var x=0;x < data.length; x++){

        var frame = Ti.UI.createView({
            width:columnWidth,
            height:columnWidth,
            backgroundColor:options.gridColor || '#eee',
            top:0,
            left:0,
            right:space,
            bottom:space
        });

        var overlay = Ti.UI.createView({
            width:Ti.UI.FILL,
            height:Ti.UI.FILL,
            backgroundColor:'transparent',
            zIndex:1,
            strImage:data[x].image
        });

        var gridElement;

        //TYPE OF LAYOUT
        switch(layout){
            case('gallery'):
                gridElement = Widget.createController('gallery',{
                    image:data[x].image,
                    title:data[x].title,
                    width:columnWidth,
                    padding:options.padding || 10,
                    showTitle:options.showTitle || false
                }).getView();
                overlay.addEventListener('click',function(e){
                    exports.openModal(e.source.strImage);
                });
                break;
            case('customView'):
                gridElement = data[x];
                break;
        }

        frame.add(gridElement);
        // This condition makes the overlay not be added if it's not gallery layout.
        // It's used to make the custom view, caputre the click method. If not,
        // The overlay is on top of it and captures the click.
        if(layout == 'gallery')
            frame.add(overlay);

        $.fgScrollView.add(frame);
    };
};

exports.openModal = function(url){
    var overlay = Ti.UI.createView({
        width:Ti.UI.FILL,
        height: Ti.UI.FILL,
        backgroundColor:'#000',
        opacity:0,
        zIndex:100
    });

    var topView = Ti.UI.createView({
        width:Ti.UI.FILL,
        height: Ti.UI.FILL,
        zIndex:1200,
        visible:false
    });

    //this gets image , adds it to top view
        var imgView = Ti.UI.createImageView({
            image: url,
            width:Ti.UI.SIZE,
            height: Ti.UI.SIZE
        });

    var button = Titanium.UI.createButton({
        title : 'Use Picture',
        top : 10,
        width : 100,
        height : 50
    }); 

    button.addEventListener('touchend',function(e)
    {

   //Titanium.API.info(url);

    if(typeof(onClickCallback) === 'function') {
        onClickCallback({ type:'clicked!' }); }

    });


    //pass callback, not working
    onClick();

    //add it
    topView.add(imgView);
    topView.add(button);
    $.fgMain.add(overlay);

    if (OS_IOS){
        //ANIMATION OF OVERLAY
        overlay.animate({opacity:0.7,duration:200});

        //ANIMATION FOR POP EFFECT
        var t = Titanium.UI.create2DMatrix();
        t = t.scale(0);
        var a = Titanium.UI.createAnimation();
        a.transform = t;
        a.duration = 200;

        $.fgMain.add(topView);
        topView.animate(a);

        a.addEventListener('complete', function(){
            topView.visible = true;
            var t2 = Titanium.UI.create2DMatrix();
            t2 = t2.scale(1.2);
            topView.animate({transform:t2, duration:200},function(e){
                var t4 = Titanium.UI.create2DMatrix();
                t4 = t4.scale(1.0);
                topView.animate({transform:t4, duration:200});

                //alert('animation complete');
                //hide cancel button


            });
        });
    }
    else{
        //ANIMATION OF OVERLAY

        overlay.animate({opacity:0.7,duration:200},function(e){
            topView.visible = true;
            $.fgMain.add(topView);
        });

    }

    topView.addEventListener('click',function(e){
        if (OS_IOS){
            var t3 = Titanium.UI.create2DMatrix();
            t3 = t3.scale(1.2);
            var a2 = Titanium.UI.createAnimation();
            a2.transform = t3;
            a2.duration = 200;
            topView.animate(a2);


            a2.addEventListener('complete', function(){
                var t5 = Titanium.UI.create2DMatrix();
                t5 = t5.scale(0);
                topView.animate({transform:t5, duration:200},function(e){
                    $.fgMain.remove(topView);
                    overlay.animate({opacity:0,duration:200},function(e){
                        $.fgMain.remove(overlay);

                    });
                });
            });
        }
        else{
            $.fgMain.remove(topView);
            overlay.animate({opacity:0,duration:200},function(e){
                $.fgMain.remove(overlay);
            });
        }

    });

};

exports.clearGrid = function(){
    $.fgScrollView.removeAllChildren();
};


// Assign our callback
function onClick(callback) {
    onClickCallback = callback;

    alert(onClickCallback);
};


// Make the onClick function public
exports.onClick = onClick;
Community
  • 1
  • 1
bobo2000
  • 1,779
  • 7
  • 31
  • 54
  • Have you tried changing the event to `onClick`: `` – Josiah Hester May 11 '14 at 03:09
  • I dont think the problem is the actual event, it is just not being called outside of exports.createGrid – bobo2000 May 11 '14 at 03:15
  • As I understand it, all the callback code should go in the original `widget.js` If you add a button eventListener manually, the `if(typeof)...` code should be within this eventListener, where you have your debug right now. However, do you create your button in xml and in the controller? – mwfire May 11 '14 at 11:52
  • yep, did that and tried doing that, but I am having trouble writing it withiin the original function as the callback code are nested functions – bobo2000 May 11 '14 at 12:44
  • mwfire, yeah - can't get it to work. Could you post a code snippet, will help a lot. Cheers for all your help – bobo2000 May 11 '14 at 15:11
  • update my code with what I have tried so far. Cheers guys. – bobo2000 May 11 '14 at 16:41
  • You don't need to call `onClick()`, but have you added `$.myWidget.onClick(function(e) { alert(e.type); });` to your `photoGallery.js`? myWidget is to be replaced with the actual id of your widget – mwfire May 11 '14 at 20:54
  • it turns out the if statement is not being called because var onClickcallback is not a function, is there any way I can test if the code is behaving properly, cheers. – bobo2000 May 11 '14 at 20:58
  • So where do you assign the callback function to `onClickCallback`? – mwfire May 12 '14 at 15:49
  • I just followed your example and added // This will hold our callback var onClickCallback; to my script, how can I create a custom callback function to do this? Sorry I am quite new to javascript and callback functions. Thanks for all of your help mwfire. – bobo2000 May 12 '14 at 16:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52541/discussion-between-mwfire-and-bobo2000) – mwfire May 12 '14 at 17:54

0 Answers0