Calling a controller function on button click

Permalink
I have a single page that shows a page list of pages 'owned' by the currently logged in user. The list displays some custom attributes such as a thumbnail. I've gotten that far without too much trouble.
My problem is that for each page I also want to display some "action buttons." One of these buttons, when clicked, would toggle a custom checkbox page attribute.

I'm not quite sure how to go about doing this.

1.) How do I call the function to set the attribute on the button click?:
$page->setAttribute('checked_attribute', true);

I know you must use javascript/jQuery as a medium, but still not quite sure how to accomplish that.

2.) I've been looking at this article:
http://www.c5tutorials.com/tutorials/ajax-ifying-your-site-part-1/...
Which is a really good starting point, but I'm not sure how to convert this to use with multiple links, each with a different variable to pass to my function (the page id to toggle the attribute on)

 
Mnkras replied on at Permalink Reply
Mnkras
yea you are going to need to use ajax, you could either use a tool, or have a method in your controller, you can also do it via get or post requests.

so you just watch of the checkbox on change, then you can ajax post the form to the action in the controller or tool.
jlego replied on at Permalink Reply
Sorry, but being a designer, my knowledge of code is rather limited. If you could, would you explain in a little more detail? Perhaps an example if it isn't too much trouble.

:)
Mnkras replied on at Permalink Reply
Mnkras
you could try something like this, but I would dig in a bit more for a customized solution.
$('.target').change(function(){
    $.post(
        '<?php echo $this->action('save_attribute')?>',
        {
         attr: $(":checked").val();
      }
   )
});
jordanlev replied on at Permalink Best Answer Reply
jordanlev
I would first try doing this without ajax -- instead just as a normal POST. After you have that working, then you can try making it work in an ajax way.

First, create an action method in the single_page controller, something like this:
public function change_my_attribute() {
    $cID = $this->post('cID');
    $newValue = $this->post('newValue');
    $c = Page::getByID($cID);
    $c->setAttribute(CollectionAttributeKey::getByHandle('my_attribute_handle'), $new_value);
}


Then, inside each item of the page list, have a small form that posts to that controller action, with a hidden fields denoting the page's cID and the new value you want to set the attribute to (either 0 or 1 for a checkbox attribute -- 0 is "unchecked" and 1 is "checked"), along with a button the user can click. For example (assuming you're using a modified version of the built-in page_list template where "$cObj" is the name of the collection/page variable inside the "for" loop):
<form method="POST" action="<?php echo $this->action('change_my_attribute'); ?>">
    <input type="hidden" name="cID" value="<?php echo $cObj->getCollectionID(); ?>" />
    <input type="hidden" name="newValue" value="1" />
    <input type="submit" value="Click Here To Change The Thing" />
</form>

(Note that if you want to have the form un-check the attribute, the value of the "newValue" hidden field would be 0 instead of 1).

Once you have that working, then you can move on to ajax-ifying it, which I can explain too, but just start with this first and get it working -- if you try to do it the ajax way from the beginning, then you will have two sets of problems to tackle instead of just one at a time, which makes it much more difficult to troubleshoot when things go wrong.

Good luck!

-Jordan
jlego replied on at Permalink Reply
Thank you, Jordan! This makes sense, I'm off to test it out. I'll post back with my results
jlego replied on at Permalink Reply
I got some errors at first but it does appear to be working now.
This line was giving me an error:
$c->setAttribute(CollectionAttributeKey::getByHandle('my_attribute_handle'), $new_value);

So I changed it this:
$c->setAttribute('my_attribute_handle', $newValue);


I did have a slight issue where even though the page was reloaded, the pages with the changed attribute were still being loaded into the list, but I needed to add the attribute filter before I looped through the pages.

Now, just have to do some clean-up and maybe see if I can get the AJAX working.

Thank you again, Jordan.
jordanlev replied on at Permalink Reply
jordanlev
To make it work with ajax, I think you can do this...

Create a new file in your site's top-level "tools" directory, for example let's call it "ajax_set_my_attribute.php". In that file put code like this:
<?php defined('C5_EXECUTE') or die(_("Access Denied."));
$controller = Loader::controller('/your_singlepage_path');
$controller->change_my_attribute();


Now make sure you're loading the jquery form plugin on the page, so add this to the controller file (not the tool or single_page):
public function view() {
    $this->addHeaderItem(Loader::helper('html')->javascript('jquery.form.js'));
}


And then you'll want to modify the single_page file in two ways. First, add a class to all of your forms, like this:
<form method="POST" action="<?php echo $this->action('change_my_attribute'); ?>" class="ajax">

Then, add this once in that single_page file:
<script type="text/javascript"> 
// wait for the DOM to be loaded 
$(document).ready(function() {
   var options = {
      url: '<?php echo Loader::helper('concrete/urls')->getToolsURL('ajax_set_my_attribute'); ?>',
      success: function() {
         //do something here with javascript to update your page display to reflect the change (c5 won't update it for you until the page is reloaded again)
      }
   };
   $('.ajax').ajaxForm(options);
});

Note that the 'ajax_set_my_attribute' thing above should be the name of your tools file without the .php extension.

May need to test this out a bit, not 100% sure it's all right, but that's the general idea.
jlego replied on at Permalink Reply
Thank you much for the tips on getting AJAX working. That saves me a lot of time! :)

However, I'm getting an error when I run the script "Call to undefined method Controller::change_my_attribute()" in the tools file.

I did change all the values/names to the ones currently being used in my code and I triple-checked the spelling and case.

Do I need to somehow load the class I've used in my controller.php to extend the controller class?
class MySinglePageController extends Controller {}
jordanlev replied on at Permalink Reply
jordanlev
Can you ZIP up your single_page, controller, and tool file and post it? (or email it to me if you don't want to post it publicly)
jordanlev replied on at Permalink Reply
jordanlev
I had an error in my code -- when you call Loader::controller() in the tools file, you need to pass it a C5 path, which always starts with a slash. So instead of this:
$controller = Loader::controller('your_single_page_handle');

...it should be this:
$controller = Loader::controller('/your_single_page');


(I went and edited my answer above so it's correct now too).
thebogdan replied on at Permalink Reply
thebogdan
jordanlev,

Thanks a lot for posting your code and explaining it! It helped me a lot in my coding.
thebogdan replied on at Permalink Reply
thebogdan
I adapted the code to make it possible for a logged in user to add himself to any group on the list. I was trying to modify the jQuery code to handle individual forms (two per line) with unique IDs, and I broke AJAX functionality. The forms still work but reload the page on form submits. Can someone (ahem jordanlev ahem) point out what's wrong with my code, please?

Here's the code for the forms:
<div class="input LeaveThisGroupButton" id="LTGB_<?php echo t($g['gID'])?>">
<form method="POST" action="<?php echo $this->action('LeaveThisGroup'); ?>"  class="ajaxLeave">
<input type="hidden" name="groupID" value="<?php echo t($g['gID']); ?>" />
<input
<?php  if (!$u->inGroup($groupObject)) { ?>
disabled="disabled"
<?php  } ?>
type="submit" value="Leave Group" class="btn"  />
</form>
</div>
<div class="input JoinThisGroupButton" id="JTGB_<?php echo t($g['gID'])?>">
<form method="POST" action="<?php echo $this->action('JoinThisGroup'); ?>" class="ajaxJoin">
<input type="hidden" name="groupID" value="<?php echo t($g['gID'])?>" />
<input
<?php  if ($u->inGroup($groupObject)) { ?>


And here's the modified JS:
$('.ajaxJoin').each(function(){
 var selectedForm = $(this);
 var optionsJoin = {
   url: '<?php echo  Loader::helper('concrete/urls')->getToolsURL('ajax_jointhisgroup');    ?>',
   success: function() {    
$(selectedForm).find('btn').attr('disabled','disabled');
   }
 };
 $(this).ajaxForm(optionsJoin);
};


Basically, I want the bound javascript to affect the form that fired it. Any help is appreciated!
jordanlev replied on at Permalink Reply
jordanlev
Sorry, I don't quite understand what you want the code to do, and why it's not working (and also where this code appears exactly in your site).

I see that in your jquery code you are only applying it to the .ajaxJoin class (which is only one of the two forms you have markup for)... perhaps this be the problem, and you need to add some jquery for the other form as well?