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?:
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)
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)
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.
:)
:)
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(); } ) });
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:
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):
(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
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):
(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
Thank you, Jordan! This makes sense, I'm off to test it out. I'll post back with my results
I got some errors at first but it does appear to be working now.
This line was giving me an error:
So I changed it this:
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.
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.
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:
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):
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:
Then, add this once in that single_page file:
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.
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:
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.
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?
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 {}
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)
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:
...it should be this:
(I went and edited my answer above so it's correct now too).
$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).
jordanlev,
Thanks a lot for posting your code and explaining it! It helped me a lot in my coding.
Thanks a lot for posting your code and explaining it! It helped me a lot in my coding.
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:
And here's the modified JS:
Basically, I want the bound javascript to affect the form that fired it. Any help is appreciated!
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)) { ?>
Viewing 15 lines of 20 lines. View entire code block.
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!
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?
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?
so you just watch of the checkbox on change, then you can ajax post the form to the action in the controller or tool.