views:

116

answers:

3

I would like to use jQuery filterJSON with flickr, and I found that it does render remote JSON files and not only local ones as shown in its author's demo page. When replacing the default path to local JSON file with remote Twitter JSON file, it renders fine. BUT when using other JSON feeds like the flickr JSON in the code below, returns undefined variable and nothing renders, why is that? I have went through the plugin code line by line many times and I still cannot understand why it only renders twitter JSON.

I must be overlooking something and would appreciate your help. Thanks a lot!

This Works:

$('body').filterJson({
 data : 'http://twitter.com/status/user_timeline/billgates.json?count=10&callback=?',
 limit : 2,
 anchorWrap : '[p]',
 nextText : 'See More',
 prevText : 'Go Back',
 transition: 'fade',
 delay : 400,

 complete : function(i, r) {
       $('body').append('[p]' + r[i].text + '[/p]')}  // [p] replace [] with brackets as the code tag does not seem to be working for me :(

This Does Not Work: ... Why?

$('body').filterJson({
 data : 'http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&id=7218238@N08&format=json&jsoncallback=?',
 limit : 2,
 anchorWrap : '[p]',
 nextText : 'See More',
 prevText : 'Go Back',
 transition: 'fade',
 delay : 400,

 complete : function(i, r) {
       $('body').append('[img src="'+r[i].items.media.m+'" /]')}  // [img /] replace [] with brackets as the code tag does not seem to be working for me :(

== EVEN deeper JSON hierarchy ==

({
    "something1": {
        "someArray": [{
            "1stproperty": {
                "text": "textstring",
            },
            "2ndproperty": "moretext",
            "image": [{
                "t1": "r1"
            },
            "t2": "r2"
            }]
        }] 
    }
})
A: 

Because you are accessing a feed, not an API with the Flickr url. The feed format needs to be either an Atom or RSS variant. It won't return a javascript call, but rather data in the chosen format.

tvanfosson
The plugin parses JSON using jQuery's getJSON so the URL JSON feed is valid.
A: 

Try this:

complete : function(i, r) {
       $('body').append('[img src="'+r.items[0].media.m+'" /]')} 

Looking at the json it seems the first one returns an array of objects (r[i]) and the 2nd only returns a single object. One of the fields in that object (items) is an array.

This should work for you. (However, I don't think the plugin will work as expected since it expects an array and is getting a single object.)

Hogan
Does not work. URL must be `jsoncallback=?` bcause the plugin is using jQuery getJSON to parse JSONP.
oops, sorry @unknown I changed my answer -- see above.
Hogan
@Hogan, thanks. It only parses the first item but it flickers and breaks pagination. I also tried r[i].items[0].media.m but this does not render anything. So, you're right, it does not work as expected. Anyway to modify the plugin to render arrays?
+4  A: 

filterJSON expects an array of items in the JSON result whereas Flickr provides an single parent object in the result which contains an array of items inside the 'items' property of that parent object.

This is an inherent limitation in filterJSON that you cannot fix without modifying the plugin itself as follows:

24a27
>      itemsLocation: null,
39a43,49
>        if (options.itemsLocation && $.isArray(options.itemsLocation)) {
>          // extract the items from the object hierarchy
>          $.each(options.itemsLocation, function () {
>            r = r[this];
>          });
>        }
>

You can then specify an array of property names (e.g. itemsLocation: ['items'] for the Flickr result) to extract the items that you want filterJSON to work with.

Working Demo

http://jsbin.com/enudu3 (editable via http://jsbin.com/enudu3)

Full Source

https://gist.github.com/290420

/*
 * filterJSON v.9.1 - http://www.jeffrey-way.com
 * Last Updated: January 29, 2009

 * When you attach a JSON file, it will parse it with getJSON, and then allow you to filter through and display the items on the page according to 
 * a specified limit, delay, transition, etc. 

 * Developed by Jeffrey Way
 * http://jeffrey-way.com/filterjson-a-jquery-plugin/
 * [email protected]
 * Modified by Brian Peiris
 * http://twitter.com/brianpeiris
*/

(function($) {

    $.fn.filterJson = function(options) {

        var defaults = {
            anchorWrap : '<p>',
            complete : null,
            data : null,
            delay : 500,
            limit : 5,
            index : 0,
            transition : 'fade',
            itemsLocation: null,

            nextId : 'next',
            nextText : 'More',

            prevId : 'prev',
            prevText : 'Last'
        },

        options = $.extend(defaults, options);

        this.each(function() {

            var $this = $(this);

            $.getJSON(options.data, function(r) {
                if (options.itemsLocation && $.isArray(options.itemsLocation)) {
                    // extract the items from the object hierarchy
                    $.each(options.itemsLocation, function () {
                        r = r[this];
                    });
                }

                // add the NEXT button
                $this
                   .after(options.anchorWrap)
                   .next()
                   .append('<a></a>')
                   .children()
                   .attr('href', '#')
                   .attr('id', options.nextId)
                   .text(options.nextText);

                // Get first set of items
                options.index = getItems(r);                

                // when the user clicks the NEXT button
                $('a#' + options.nextId).live('click', function() { 

                    // If a previous button doesn't exist, create it!
                    if($('a#' + options.prevId).length == 0) {
                        $this
                           .after(options.anchorWrap)
                           .next().append('<a></a>')
                           .children()
                           .attr('href', '#')
                           .attr('id', options.prevId)
                           .text(options.prevText);                 
                    }

                    // If the user chose the "slide" transition, slideUp...otherwise fadeOut.
                    if(options.transition === 'slide') $this.slideUp(options.delay, emptyAndContinue);
                    else $this.fadeOut(options.delay, emptyAndContinue);        

                    // remove the current items and get the next set, passing in the json file - represented by "r"
                    function emptyAndContinue() {
                        $this.empty();
                        options.index = getItems(r);
                    }                   

                    return false;
                }); // end nextId click     


                // When the previous button is clicked, add the NEXT button if one doesn't exist.
                $('a#' + options.prevId).live('click', function() {
                    if($('a#' + options.nextId).length == 0) {
                        $this
                           .after(options.anchorWrap)
                           .next().append('<a>')
                           .children()
                           .attr('href', '#')
                           .attr('id', options.nextId)
                           .text(options.nextText);
                    }   

                    // If the user chose the "slide" transition, slide up...oitherwise fadeOut
                    if(options.transition === 'slide') $this.slideUp(options.delay, emptyAndContinue);
                    else $this.fadeOut(options.delay, emptyAndContinue);

                    // remove the current items and get the next set, passing in the json file - represented by "r"
                    function emptyAndContinue() {
                        $this.empty();
                        options.index = getPreviousItems(r);                        
                    }                   

                    return false;                   
                }); 

                return this;

            }); // end getJSON

            // Acccepts the parsed JSON file as a parameter "r", and then gets the next set of items. 
            function getItems(r) {  

                var i = options.index, // the current index, or offset. 
                    dataLength = r.length; // total # objects in the JSON file.

                    (function append() {            
                        if(i === dataLength) {$('a#' + options.nextId).remove(); return; } // if the index is equal to total # of items in JSON file, remove the "next" link and exit.
                        if(i >= options.limit + options.index) return; // if index is gte to the current index + the limit, return because reached the max.

                        options.complete(i, r); // call userdefined "complete" function, which appends necessary html to the page.

                        // If the user chose the "slide" transition, slide up...oitherwise fadeOut                      
                        if(options.transition === 'slide') $this.slideDown(options.delay);
                        else $this.fadeIn(options.delay);
                        i++;
                        append(); // Rinse and repeat until i equals the limit          
                    })();   

                // Increase INDEX by current offset to allow for the next grouping.
                return options.index + options.limit;
            } /// end getItems


            // Used when the PREVIOUS BUTTON is clicked. 
            function getPreviousItems(r) {
                // Need to reduce the current options.index back to what it would have been on the previous page. A bit confusing...
                var i = options.index - options.limit * 2;          

                (function append() { // if the user has chosen to delay the appearance of each new image...     
                    if(i === 0) $('a#' + options.prevId).remove(); // If i = 0, we don't need a previous button.        
                    if(i >= options.index - options.limit) return;

                    options.complete(i, r); // call userdefined "complete" function, which appends necessary html to the page.

                    if(options.transition === 'slide') $this.slideDown(options.delay);
                    else $this.fadeIn(options.delay);
                    i++;
                    append(); // Rinse and repeat until i equals the specified options.limit.
                })();       

                // REDUCE index by the limit, because "PREVIOUS BUTTON" was clicked
                return options.index - options.limit;
            } /// end getPreviousItems



        }); // end this.each



    };  // end plugin. Go eat some cake.

})(jQuery);
brianpeiris
@brianpeiris, excellent work! Anyway to make it append new items from the feed in real-time (by using `setInterval` or `setTimeout`) or a better method? It would be cool to have asynchronously append new additions with slideDown effect. Any ideas?
+1 nice job brian. Small bug I fixed http://jsbin.com/enudu3/3 You had itemsLocation being an array but then only used the last element of the array. I changed it to just a name. Java code is here http://gist.github.com/290440
Hogan
I'm not entirely sure what you're asking for (what do you mean by "in real-time"?) but filterJSON doesn't seem do anything more than what the demo shows. You'd have to create a custom solution for yourself or make further extensive modifications to filterJSON. I'll leave that as an exercise for you ;) Although you're always welcome to post a new question on SO if you need help.
brianpeiris
@Hogan I'm afraid you've misunderstood my code. `itemsLocation` was intentionally an array; you can specify multiple property names in the array and the loop will iterate down the object hierarchy of the JSON result to find the items you want to work with.
brianpeiris
@brianpeiris, thanks a lot! I will rephrase my "real-time" request in a different question. But one last thing, since you fixed the plugin to work with sophisticated hierarchy but it only applies to arrays, anyway to make it work with even deeper hierarchy like the sample JSON code I posted on the bottom of my question. Thanks again!
@brian Mea culpa, I see what you mean now. Nice trick.
Hogan
The code should handle that. Assuming you want filterJSON to work with the "image" array, just set `itemsLocation: ['something1', 'someArray', 0, 'image']` and it should work. P.S. Consider giving your SO account a name!
brianpeiris
Works like a charm! Thank you very much for all your help. +1 and check for you sir!