Referencing the calling button in a click binding

182 views
Skip to first unread message

EdSilv

unread,
Feb 25, 2011, 11:57:46 AM2/25/11
to KnockoutJS, e...@unwrong.com
Hi,

I'm making a control to page through members of a Facebook group.

It retrieves a page of data from FB and displays it in a list with a
"More" button underneath.

When clicking on "More" it fetches the next page and displays the
results.

The problem is I can't figure out how to hide the "More" button when
it's clicked.

I can't hack it with jQuery to target the button's class because that
would hide all buttons with that class not the specific one that was
clicked.

Here's the template:

templateEngine.addTemplate("ko_fbMembersControl", "\
<div id=\"membersScroll\" style=\"overflow:auto; height:200px
\">\
{{each(i, item) data}}\
<div style=\"height: 80px; border-top:1px solid black;\">\
<img border=\"none\" src=\"https://graph.facebook.com/$
{id}/picture\" />\
<p>\
${name}\
</p>\
</div>\
{{if (i+1)/5 == Math.floor((i+1)/5)}}\
<button class=\"moreButton\" data-bind=\"click:
function() {\
nextPage(function(response){\
scrollTop = $
('#membersScroll').scrollTop();\
for(i=0; i < response.length; i++){\
data.push(response[i]);\
}\
$('#membersScroll').scrollTop(scrollTop);\
$('.moreButton').hide();\
});\
}\">\
More\
</button>\
{{/if}}\
{{/each}}\
</div>\
");


Where it says:

$('.moreButton').hide();

I want this to be a reference to the actual clicked button instead.
How do I pass a reference to the clicked button into the handler? Is
there already a util for getting this?

Regards,

Ed

rpn

unread,
Feb 25, 2011, 12:24:56 PM2/25/11
to knock...@googlegroups.com, e...@unwrong.com
I could see two ways to do this pretty easily.  

The first is to put an observable on your item that tracks whether or not there is "more".  Then, use the visible binding on your button against that observable.  This feels like the "Knockout" way to do it.

The second would be to look at the event that gets passed into your function.  You would need to be using the latest knockout code (not 1.12).  The jQuery event object actually gets passed to your function, if you want it.  From there, you could check the target.  Usually preferrable if your viewModel doesn't have any knowledge of the actual UI.  Although, I guess since you are already calling an anonymous function, you could argue that your viewModel doesn't know about it in this case :)


EdSilv

unread,
Feb 25, 2011, 1:13:04 PM2/25/11
to KnockoutJS
Cool, thanks for that. Wow, jsfiddle is awesome.

I only display the More button for every 5 (or whatever) results. Not
for every item in the array of members. I initially retrieve the first
page of FB data and make it observable using ko.mapping.fromJS(). Then
when I subsequently retrieve new data I just do a for loop through it
adding to the existing observable array.

When this is rendered in the template it checks for every fifth item
and puts in a More button with a binding to the viewModel's nextPage()
function.

I'm not sure how I would extend the items in the members array to
support this moreWithEvent function or if I'd want to really.

Are you sure there isn't a util or trick of some kind to get a
reference to the button in the inline handler rather than adding to
the viewmodel?

rpn

unread,
Feb 25, 2011, 1:27:25 PM2/25/11
to knock...@googlegroups.com
Not sure of your exact structure, but you could just put the "showMore" observable on all items and initialize it to false on all, but every 5.  You could also just put it on all items and only interact with it for every 5 in the same way that you are doing it now in the template.  I could be misunderstanding your structure.

As for a trick, the sample is updated to show calling it anonymously, if that is the way that you have to do it.  I think that should work for you.  http://jsfiddle.net/rniemeyer/YUhzk/ .  This would be like passing arguments[0] as the first param of nextPage().

EdSilv

unread,
Feb 26, 2011, 10:12:47 AM2/26/11
to KnockoutJS
Cool, I think that's nearly the solution.

For some reason arguments[0] is undefined.

<button class=\"moreButton\" data-bind=\"click: function(){\
log(arguments);\
nextPage(function(response){\
scrollTop = $('#membersScroll').scrollTop();\
for(i=0; i < response.length; i++){\
data.push(response[i]);\
}\
$('#membersScroll').scrollTop(scrollTop);\
});\
}\">\
More\
</button>\

The log(arguments) yields [[]]

Could it be that the arguments aren't there because I'm defining the
template as a string with ko.jqueryTmplTemplateEngine? (Hence the
escaped quotes and line endings).


On Feb 25, 6:27 pm, rpn <rnieme...@gmail.com> wrote:
> Not sure of your exact structure, but you could just put the "showMore"
> observable on all items and initialize it to false on all, but every 5.  You
> could also just put it on all items and only interact with it for every 5 in
> the same way that you are doing it now in the template.  I could be
> misunderstanding your structure.
>
> As for a trick, the sample is updated to show calling it anonymously, if
> that is the way that you have to do it.  I think that should work for you.  http://jsfiddle.net/rniemeyer/YUhzk/.  This would be like passing

rpn

unread,
Feb 26, 2011, 11:24:13 AM2/26/11
to knock...@googlegroups.com
Not sure.  Are you possibly able to reproduce it in JSFiddle?  

You could start with this one: http://jsfiddle.net/rniemeyer/KtR2b/.  I tried to simulate creating a template in the way that you did.

EdSilv

unread,
Feb 26, 2011, 4:07:59 PM2/26/11
to KnockoutJS
Ok, made a slightly modified version to work with a FB page's wall
feed:

http://jsfiddle.net/edsilv/jQJ6y/

I didn't have any problems accessing the arguments array in jsfiddle.
I think it must be because I'm using the latest versions of the
knockout libs.

So, I could then access the button and hide it. But, this was no good
anyway, as when the feed is re-databound it just displays the more
buttons again.

The problem is with this logic in the template:

{{if (i+1)/5 == Math.floor((i+1)/5)}}\

I need to show the more button when it reaches the last item in
"data".

Like this:

{{if i == data.length - 1}}\

Except, data always seems to be 0 length..!

Do I need to do something to the data variable in order to find out
how many items there are?

Thanks for all your help by the way :-)

EdSilv

unread,
Feb 26, 2011, 5:26:33 PM2/26/11
to KnockoutJS
Nevermind, fixed it.

Needed to use ko.utils.unwrapObservable().

:-)

rpn

unread,
Feb 26, 2011, 5:57:55 PM2/26/11
to knock...@googlegroups.com
Yes, if data is an observableArray, then you can do data().length or calling ko.utils.unwrapObservable would have the same affect.
Reply all
Reply to author
Forward
0 new messages