Numbering Kirby Modules and Kirby Builder structure field

:sob:

(I should give back more to this forum)


TL;DR: How can you get the element number of a field part of a structured field?

$section->num() does not obv work since it’s not a page.


I am building a multi-nested survey and want to build it and manage it from the panel. I am using Kirby Modules (w/ Kirby Sortable) together w/ Kirby Builder. And, I am nesting a modules inside another modules, though I read it’s not meant to work like that.

I’d like to number each module and section (which is part of a structure field from Kirby Builder), by combining the module number with the section number, eg:

1. Question A
   1.1 Question B
   1.2 Question C

I am using this for creating unique ids and names for the input tag, not for numbering a list item.

I can do the module numbering easily, since it is a subpage, by echoing

<?php echo $module->num() ?>

But when I try to call again the above code in the snippet (section) field, I get an error of unknown variable.

Is there a way to traverse and going back from $data() (the $section object) to $module? I tried to use $parent() without success.

Using $section->num() (actually $data->num()) does not output anything, which I understand, since it’s not a page (unlike $module).

I can use $section->count() to get the number of objects in the collection, but how can I get the correct number for each element? I tried to use ->current() (not knowing what it does), and it outputs NO.

Furthermore: I am up to simplify this whole structure, by not nesting a $module inside a $module, and simply creating a fieldset for Kirby Builder called fieldname_child and then in its snippet syntax, building the correct numbering if that’s easier.

Try indexOf($needle): https://getkirby.com/docs/toolkit/api/collection/index-of

1 Like

That’s great, but in my case I am using the data snippet function, and cannot provide a $collection object.

If I do this anyway, $section is unknown.

<?php echo $section->indexOf($data) ?>

Also if I do

<?php echo $data->indexOf($data) ?>

What am I missing?

Note that if you use IDs starting with a number in your HTML, you have to escape them in your stylesheets:

#11 {
 /* won't work */
}

#\11 {
/* will work */
}

[id='11'] {
 /* will work */
}

Thanks for that tip, the prefix I use for the numbering is like:

  • q_ for questions
  • a_ for answer options
  • m_ for inline messages (jump to question X if you replied Yes to this question)

Yes, indexOf() only works with a collection.

Could you post a bit more context, not just that single code line?

Sure!

So my modules in the template page

<ol>
   <?php echo $page->modules(); ?>
</ol>

I have this to output all the sections building a module

<li>
  <fieldset class="bd--a_red pd--v_1" id="<?php echo $module->num() ?>">
    <?php echo $module->num() ?>
    <?php foreach($module->builder()->toStructure() as $section): ?>
      <?php snippet('sections/' . $section->_fieldset(), array('data' => $section)) ?>
    <?php endforeach ?>
  </fieldset>
</li>

This is an example of a snippet building a section (with a try out to get the parent num()).

<input type="text" id="<?php echo $data->parent()->count() ?>" 
name="q_<?php echo $data->count() ?>_" placeholder="" 
value="<?= isset($fill['firstname']) ? $fill['firstname'] : '' ?>" 
<?php e($data->required() != 'no', 'required') ?> 
class="<?php echo $data->_fieldset() ?>" />

I was thinking, what I keep count of each module as it is rendered in the page by saving its position in a variable. Is that possible? Sort of like making an index in js.

Also, I had to change this part and replace the original $data with another name variable ($fill).

value="<?= isset($fill['firstname']) ? $fill['firstname'] : '' ?>" 

I had to do this because of Kirby Builder’s usage of the $data variable.
I still have to test this change, but is it going to be a problem?

Well, $module->builder()->toStructure() should give you a collection:

<?php $collection = $module->builder()->toStructure(); ?>
<li>
  <fieldset class="bd--a_red pd--v_1" id="<?php echo $module->num() ?>">
    <?php echo $module->num() ?>
    <?php foreach($collection as $section): ?>
      <?php snippet('sections/' . $section->_fieldset(), array('data' => $section, 'collection' => $collection)) ?>
    <?php endforeach ?>
  </fieldset>
</li>

So you should be able to get the number of the section here (not tested):

<input type="text" id="<?php echo $collection->indexOf($data) ?>" 
name="q_<?php echo $collection->indexOf($data)?>_" placeholder="" 
value="<?= isset($fill['firstname']) ? $fill['firstname'] : '' ?>" 
<?php e($data->required() != 'no', 'required') ?> 
class="<?php echo $data->_fieldset() ?>" />

Unfortunately it still throws at me

Undefined variable: collection

I was thinking of using a panel.page.update hook that would save a new field entry for every fieldset that Kirby Builder creates and moves around in terms of order of position, but I think I might be back to square one.

Still, I am going to browse Kirby Builder’s source code to see if there’s something I can use.

Have you passed the variable to the snippet?

Yay, indeed by adding collection' => $collection it works

<?php snippet('sections/' . $section->_fieldset(), array('data' => $section, 'collection' => $collection)) ?>

Now looking up how to make start the index at 1.

Ha, you did not copy my code snippet :worried: Just add +1 to the index :wink: (and some more chars)

I’ve been fooled by the code-block clipping out part of the code, I now see you added the second variable to the snippet!

Thanks for the tip for customising the index, works perfectly. And thanks.

Hi,

I’m also trying to start my index at 1 not 0 and I can’t for the life of me figure it out.

<div class="wrap">
<div class="footnote_wrap">
    <h4 class="footnote_header">Illuminations & References</h4>
    <?php foreach ($page->footnotes()->toStructure() as $footnote): ?>
    <span class="footnote_number"><?= $footnote->indexOf(1, $footnote) ?><p class="footnote"><?= $footnote->footnote() ?></p></span>
    <?php endforeach ?>
</div>
</div>
<div class="wrap">
<div class="footnote_wrap">
    <h4 class="footnote_header">Illuminations & References</h4>
    <?php $footnotes = $page->footnotes()->toStructure(); ?>
    <?php foreach ($footnotes as $footnote): ?>
    <span class="footnote_number"><?= $footnotes->indexOf($footnote)+1 ?><p class="footnote"><?= $footnote->footnote() ?></p></span>
    <?php endforeach ?>
</div>
</div>

indexOf() has to be called on the collection of structure items, not on a single item. So the single items is passed as a parameter, because that’s the one of which you want to get the position in the collection.

1 Like

Brilliant. Thank you.