Dynamically(via javascript) add page selectors in block add/edit form.

Permalink 1 user found helpful
Right now I'm doing this stupidity because I can't figure out how to add the sitetree page selectors dynamically in a block add/edit form.
<?php echo $form->label('page_list_parent_id[0]', t('Page List 1')); ?>
<?php echo $page_selector_form->selectPage('page_list_parent_id[0]', $parent_id[0]); ?>
<?php echo $form->label('page_list_parent_id[1]', t('Page List 2')); ?>
<?php echo $page_selector_form->selectPage('page_list_parent_id[1]', $parent_id[1]); ?>
<?php echo $form->label('page_list_parent_id[2]', t('Page List 3')); ?>
<?php echo $page_selector_form->selectPage('page_list_parent_id[2]', $parent_id[2]); ?>

It works fine for me personally since if I ever need more I can just add to the add/edit.php but it would be nice to add and remove these dynamically so that this can be a little more re-usable for others.

View Replies: View Best Answer
PineCreativeLabs replied on at Permalink Reply
I'm actually wondering the same thing for my blocks. Kind of like an "Add Item", with ability to remove it, too. I wish I knew how to do this for users' sakes!

I have tried looking at Image gallery type blocks to see how it's done, as a sort of model, but I still don't understand everything that's going on with that stuff. Maybe I'll figure it out soon.
JohntheFish replied on at Permalink Best Answer Reply
I have not tried it, but wouldn't it just be a variation of any extending form array trick? as in

Once the jQuery has cloned the row, the most I would expect to have to do is tweak an element before re-writing it to catch any duplicated id="" or name="".
mkly replied on at Permalink Reply
Indeed. I've actually been using that very helpful howto as a base of what I'm trying to do. The trick has been extending that form with the events. There are a couple of events bound to it. One being the click of "Select Page" and one being the click of the Trash can. When I clone the row, if I'm not mistaken, I think I have to bind those new elements, which I can use a $.live() type approach. I'm just having a tough time figuring out which events to bind.

The first is the dialog popup, and the second is the trash can. I guess I can take a look at the form helper and probably pull out the trash can code. And I'm just not overly versed in using the concrete5 dialog popup. Is there a howto of that lying around?

Btw, those two howtos(the add row and sortable) are great work. Thank you for those.
mkly replied on at Permalink Reply
Ok. Well if I run

after creating the page selector via the method describe in the howto(except I used siblings instead of closest) I get a new select box. All that is left is to bind an even to the trash can that removes the selector instead of just removing the page selection. I guess I'll post the snippet when I'm done.

Thank you.
kirkroberts replied on at Permalink Reply
mkly: Thanks for posting about

That did the trick for me!
jordanlev replied on at Permalink Reply
Hey John,
I found a more efficient way to dynamically change the selected page of the Page Selector widget via JS:

It works in, haven't tested in earlier versions.

Just mentioning it here in case you like that better and want to update your how-to with it. (But I won't be offended if you like everything the way it is already :)
jordanlev replied on at Permalink Reply
Oh wait, I just realized that in your how-to, you're overriding the built-in ccm_selectSitemapNode function (in concrete/core/helpers/form/page_selector.php), so you actually still need to define the javascript functionality itself.

Weird that C5 defines the function body in this way and doesn't just have the function defined for utility purposes and then either call that utility or your own callback when needed (not sure if that makes sense).

Carry on then.
JohntheFish replied on at Permalink Reply
Hi Jordan,

Its interesting stuff and stuff that I will find useful, but I think that (unless I am cleverer I thought I was) you may be crediting me with someone else's howto.

The howto I link above is purely about making a growing form.

jordanlev replied on at Permalink Reply
Ohh... I was looking at this one:

...which is by Mnkras. Not sure why I thought that was you (maybe linked from some other post). Okay well everything I've said here today is wrong then. Meh, Friday.
jordanlev replied on at Permalink Reply
Here's how I do it on custom image galleries I build for clients. Note that this is just the code for getting the page selectors to work, not the code that adds and removes rows from the list (I usually base that off of the slideshow block code). I struggled with the built-in page selector control but could never get it to work, so I kind of rolled my own page selectors (it uses the C5 sitemap popup of course, I'm just talking about the buttons that users click to open that up).

First, in each "row", I have this:
   Link To Page:
   <span class="gallery-selected-page-name" data-slideshowImgId="<?php echo $imgInfo['slideshowImgId']; ?>" style="font-weight: bold;">
      <?php echo ($imgInfo['linkToCID'] > 0) ? Page::getByID($imgInfo['linkToCID'])->getCollectionName() : ''; ?>
   <span class="gallery-clear-selected-page" data-slideshowImgId="<?php echo $imgInfo['slideshowImgId']; ?>" style="display: <?php echo ($imgInfo['linkToCID'] > 0) ? 'inline' : 'none' ; ?>;">
      [<a href="#" onclick="clearPageSelection(<?php echo $imgInfo['slideshowImgId']; ?>); return false;">X</a>]
   <span class="gallery-select-page-wrapper" data-slideshowImgId="<?php echo $imgInfo['slideshowImgId']; ?>" style="display: <?php echo ($imgInfo['linkToCID'] > 0) ? 'none' : 'inline' ; ?>;">
      dialog-title="Choose Page"

($imgInfo is an array that contains data about the one particular image that this "row" is for).

Then I have this code in one place on my add/edit page (so this is used by all the rows, not repeated for each):
<script type="text/javascript">
var selectSitemapNodeLastCalledBySlideshowImgId = null;
//Put all of this stuff into a function that gets called once when the edit page first loads,
// and then again every time an image row is added by auto.js's addNewImage() function.
function initializePageSelectors() {
   $('a.gallery-select-page').click(function() {
      selectSitemapNodeLastCalledBySlideshowImgId = $(this).attr('data-slideshowImgId');
   //Not sure why this can't just be declared once...
   // but new image rows weren't calling it for some reason,
   // so we'll just keep redeclaring it every time.
   ccm_selectSitemapNode = function(cID, cName) {
      $('input.gallery-selected-page-cid[data-slideshowImgId="' + selectSitemapNodeLastCalledBySlideshowImgId + '"]').val(cID);

Not saying this is the best way to do it, but maybe between the slideshow block and this it can help you understand the necessary process better?
jordanlev replied on at Permalink Reply
Oh, by the way (since we have a meeting of the minds here), I'm really hoping to one day build this "repeating rows" functionality in a clean and , re-usable way. So you could just put one line of code (with a bunch of config options) into your add/edit form and it would handle all of this for you.

What do you think such a widget should include? Off the top of my head, I'd think there should be one specifically for files, and then one that's more generic and you could put any other fields in. The display options could be to include a sitemap selector, an image selector, etc.

One thing that weirds me out about the way the built-in slideshow block does things is instead of just adding a row and then choosing an image for that row, you add the image and that adds the row. I guess this is a little more efficient for the user because it's just one click (and perhaps more importantly, it lets you choose more than one image from the file manager at a time, which is really cool). But for a more general-purpose widget like this, I wonder if it would be better to just have file selector and sitemap selector as options that could be on a row, and you just add blank rows regardless of what's in them?

Does any of this even make sense? What do you think? Want to help build something like this after the new year?

JohntheFish replied on at Permalink Reply
I think the base widget should be an extending/sorting list widget, with file/image/page/user... list builders being specific layers on top of that, instantiating the list widget with the existing C5 helpers.

Trigger jquery events or callbacks (or both) for add/remove/sort (for list items) and open/close/select/cancel of inner C5 helpers should give enough hooks for integrating functionality into the list.

Whilst the current thinking is rows of a table for form elements, what is actually within a row should be any DOM element, so maybe the entire structure should be div based rather than table based, that way it could format the list across a page or flow and wrap down a page.

Such a flexible list widget may also be useful for front end functionality, not just add/edit and dashboard forms.
mkly replied on at Permalink Reply
Jeez guys. Thanks for all the great info. It's really helpful. I'm slowly getting my head around it. My js-fu isn't the best.

As for clean and re-usable I would love to see an outside core addon library. Something that could evolve outside the core but be a somewhat standard toolkit for addons.

I'll post back when I get it together.

JohntheFish replied on at Permalink Reply
A gotcha with the existing C5 dialog (don't know about 5.5) is that it destroys its dom when closed, so repeated open/close/open can fail unless underlying dom is cloned and repaired outside of the dialog. Maybe that is what you are running in to.

On the bricks and paving games I cloned the dialog elements before opening, stored the clone, then rebuilt them where needed after closing.