Hi Pete,
Let me take them one by one.
“When is a repeater row considered as having a value? Does a default of 0.0 count?”
0.0 counts as a real value — the user does not have to visit or change it. Under the hood, Pro Forms only skips writing to the repeater when the whole submitted repeater dataset is empty (every sub-field of every row blank). It does not drop individual rows, and it does not treat 0.0 as empty (only a truly blank field, "", is empty).
One small edge to keep in mind: PHP considers the string "0" empty, but "0.0" is not empty. Since you’re already defaulting to 0.0, you’re on the safe side — those rows will be saved as submitted.
The important architecture point for “show all amenities whether selected or not”
The Pro Forms Repeater field is user-driven: it renders one row and the user adds/removes rows with the +/− buttons. It won’t automatically render one row per taxonomy term. So for your goal — every amenity always visible with its own distance input — the cleaner build is not the Repeater field, but:
- Loop your taxonomy terms in the builder (query loop) and output one Number field per term, each with default
0.0.
- Give each Number field a predictable Custom ID (Number field → General → Custom ID), e.g.
amenity_{term_id}. That way the submitted input name is form-field-amenity_123 and you can map it back to the term.
- On submit, a small snippet reads those fields and writes them into the ACF repeater with ACF’s own
add_row().
Yes, you’ll need a snippet for the save — here’s the shape of it:
add_action('bricksforge/pro_forms/post_created', function ($post_id) {
$terms = get_terms(['taxonomy' => 'amenities', 'hide_empty' => false]);
foreach ($terms as $term) {
$key = 'form-field-amenity_' . $term->term_id; // matches your Custom ID
$distance = isset($_POST[$key]) ? floatval($_POST[$key]) : 0.0;
add_row('amenity_distances', [ // your ACF repeater name
'amenity_id' => $term->term_id, // sub-fields
'amenity_name' => $term->name,
'distance' => $distance,
], $post_id);
}
});
Because you’re building the rows yourself here, you decide what counts — the snippet above saves every term unconditionally (even 0.0), which is exactly the “show/save them all” behaviour you’re after.
For the Edit form, this pairs nicely with what we discussed before: point the Repeater field’s Data Source at the ACF repeater to pre-load the stored rows, or (if you keep the one-field-per-term layout) read the saved values with ACF and set them as the field defaults, then write back with update_row() in the same style.
“Is selecting multiple items in a repeater the same as clicking the +/− buttons?”
No — those are two different things. The Pro Forms Repeater is a row template: each +/− click adds or removes one full row of the field group you built inside it. A multi-select field, by contrast, just stores several selected values inside that single field — it does not create repeater rows. So a multi-select ≠ multiple repeater rows.
“Is there a developer guide for creating form elements, like Bricks has for elements?”
Honest answer: there isn’t a public developer guide / registration API for building brand-new Pro Forms field types the way Bricks documents its element API. Pro Forms is extended through action/filter hooks rather than by registering custom field elements — the ones most relevant to you are bricksforge/pro_forms/custom_action (run your own PHP on submit with full access to the form via the passed $form object), plus lifecycle hooks like bricksforge/pro_forms/post_created (used above), bricksforge/pro_forms/post_updated, and bricksforge/pro_forms/validate. For custom output you’d typically use a query loop + existing fields (as above) or a Code element, rather than a new field type.
On the “one field per term without a snippet” idea
I really like where you’re going with this — needing a snippet just to seed one row/field per taxonomy term is a rough edge. I’ll happily take that to the team as a feature request: a Taxonomy-terms data source for the Repeater field — essentially “list every term of this taxonomy as a row” natively, so the initial mapping works without any custom code. No promise on timing, but it’s exactly the kind of gap worth closing, and I’ll log it.
Hope that clears it up — happy to look at your concrete form setup if you want a second pair of eyes.
Best,
Daniele