A little background about the problem first.
As mentioned in the docs on DBF (http://docs.couchcms.com/concepts/databound-forms.html), apart from using type 'bound' inputs to capture data into editable regions, we can also explicitly set them through direct parameters of <cms:db_persist_form> (and its non-DBF counterpart <cms:db_persist>) tag e.g. as follows where 'my_field' and 'my_other_field' are the names of some editable regions -
This technique, however, was not applicable to repeatable-regions as they are composed of multiple editable regions in multiple rows.
With the current version of Couch (GitHub v2.1b of the day of this post), this finally becomes possible.
For our tests, let use a non-clonable template defining only a single repeatable-region within it with three fields as follows -
Register the template by visiting it as super-admin on the front-end.
Then coming back to the admin-panel, add some data to the repeatable-region we have.
For example, my system at this points looks like this -
With the setup complete, let us begin our tests.
Add the following statement in the body of our test template -
I am sure you are familiar with <cms:show_repeatable> tag used to display data from repeatable-regions.
The 'as_json' parameter, however, would be new for you. That is because it has just been added to v2.1b.
Refreshing the page, you'll see the template outputting something like this -
That, of course, is the JSON representation of the data currently contained in our repeatable-region.
To make the data more readable, I used an online formatter (https://jsonformatter.curiousconcept.com/) and got the following -
With the structure now more legible, I think it should be easy to figure out that it is an array where each array element represents one row from the repeatable-region and that each row consists of the names of the editable-regions with their values.
I am stressing on this format because to explicitly set data for repeatable-regions, we'll need to specify that data in exactly the format seen above.
To see that happening, let us add a Data-bound Form to the template -
As you can see, it is a regular DBF. We have attached it to the template it is placed in and at this point there are no type 'bound' inputs within it. The intention of this post is to see how we can explicitly set the data for a repeatable-region, so the action we are interested in would lie almost entirely in the <cms:db_persist_form /> statement within the success block of the form.
To see that clearly, empty our repeatable-region by deleting all rows currently in it and save.
Now modify our form to make it as follows -
As you can see, we have made two changes -
1. Added a <cms:capture> block just above the <cms:db_persist_form> statement.
This block sets a variable named 'my_data' using the same JSON that we saved earlier.
2. Explicitly set my_repeatable=my_data within <cms:db_persist_form> (which is what the entire exercise is all about).
Submit the form and in the admin-panel you should see that our three original rows have been restored using the explicit data we passed to <cms:db_persist_form>.
Of course, in a real-world scenario, we'll be crafting our own JSON instead of using hard-coded data as in our example above but this should serve as a clear example of what needs to be done.
Continuing using the hard-coded JSON data - press submit once again. What do you think will the repeatable-region (which at this point has three rows) end up containing now? Take a look and you'll find that no matter how many times you submit the form, the repeatable-regions always ends up having the same three rows.
That is because the value we pass on to <cms:db_persist_form> completely overwrites whatever existing data is contained in the repeatable-region.
If your use-case dictates that each form submission preserves the existing data and appends the new data as a new row, you can use the following method -
The code requires some explanation.
The first <cms:capture> sets our 'my_data' variable by reading in the current data from the repeatable-region.
So we begin with 'my_data' as an array containing all the current rows of the region.
Look carefully at the second <cms:capture> block - the variable it is setting has a trailing '.' (dot).
As explained in the docs on "Multi-value variables (or Arrays)" viewtopic.php?f=5&t=10892, the dot serves to add a new row (unnamed key, actually) to an existing array.
So basically the second <cms:capture> appends our (hardcoded at this point) data as a new row to the existing rows of the repeatable-region.
Try it and you'll see that each submission adds a new row while preserving the existing rows.
Up until this point, we have been working with hard-coded JSON.
It is finally time to use submitted data to create JSON dynamically.
Add three inputs to the form as follows -
Please notice that these are not 'bound' inputs. Just plain cms:inputs with parameters that exactly match those used by the constituent editable regions used within our repeatable-region.
As we know, upon successful form submission, Couch will make available the contents of these inputs as variables with 'frm_' prepended to their names. We'll now make use of those variables to make our JSON dynamic as follows -
The code above should serve as the final template for you to generate your JSON (names of fields in quotes followed by their values in quotes and no comma after the last field) but there is a potential problem that needs to be addressed here before we call it quits.
JSON format is pretty unforgiving and explicitly forbids several character in its input. According to http://json.org/, following characters must be escaped before being used in JSON strings
To see what I mean, try inputting hello \ world in the text box. The resulting row will come up blank as the generated JSON is considered malformed.
To prevent that from happening, we'll make a final change to our code -
Instead of quotes around the values, we use <cms:escape_json></cms:escape_json> instead. This tag takes care of escaping all JSON problem characters (and also surrounds the resulting string in quotes - which is why we are no longer using quotes ourselves).
And that is it. Final code stands as follows -
Hope this helps.
As mentioned in the docs on DBF (http://docs.couchcms.com/concepts/databound-forms.html), apart from using type 'bound' inputs to capture data into editable regions, we can also explicitly set them through direct parameters of <cms:db_persist_form> (and its non-DBF counterpart <cms:db_persist>) tag e.g. as follows where 'my_field' and 'my_other_field' are the names of some editable regions -
- Code: Select all
<cms:db_persist_form
my_field="hello world"
my_other_field=some_variable
/>
This technique, however, was not applicable to repeatable-regions as they are composed of multiple editable regions in multiple rows.
With the current version of Couch (GitHub v2.1b of the day of this post), this finally becomes possible.
For our tests, let use a non-clonable template defining only a single repeatable-region within it with three fields as follows -
- Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template>
<cms:repeatable name='my_repeatable' order='-2'>
<cms:editable name='my_text' type='text' />
<cms:editable name='my_checkbox' type='checkbox' opt_values='Newspaper || Website || Phone Book' />
<cms:editable name='my_dropdown' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' />
</cms:repeatable>
</cms:template>
<?php COUCH::invoke(); ?>
Register the template by visiting it as super-admin on the front-end.
Then coming back to the admin-panel, add some data to the repeatable-region we have.
For example, my system at this points looks like this -
With the setup complete, let us begin our tests.
Add the following statement in the body of our test template -
- Code: Select all
<cms:show_repeatable 'my_repeatable' as_json='1' />
I am sure you are familiar with <cms:show_repeatable> tag used to display data from repeatable-regions.
The 'as_json' parameter, however, would be new for you. That is because it has just been added to v2.1b.
Refreshing the page, you'll see the template outputting something like this -
- Code: Select all
[{"my_text":"One","my_checkbox":"Newspaper","my_dropdown":"-"},{"my_text":"Two","my_checkbox":"Website","my_dropdown":"1"},{"my_text":"Three","my_checkbox":"Newspaper|Phone Book","my_dropdown":"2"}]
That, of course, is the JSON representation of the data currently contained in our repeatable-region.
To make the data more readable, I used an online formatter (https://jsonformatter.curiousconcept.com/) and got the following -
- Code: Select all
[
{
"my_text":"One",
"my_checkbox":"Newspaper",
"my_dropdown":"-"
},
{
"my_text":"Two",
"my_checkbox":"Website",
"my_dropdown":"1"
},
{
"my_text":"Three",
"my_checkbox":"Newspaper|Phone Book",
"my_dropdown":"2"
}
]
With the structure now more legible, I think it should be easy to figure out that it is an array where each array element represents one row from the repeatable-region and that each row consists of the names of the editable-regions with their values.
I am stressing on this format because to explicitly set data for repeatable-regions, we'll need to specify that data in exactly the format seen above.
To see that happening, let us add a Data-bound Form to the template -
- Code: Select all
<cms:set submit_success="<cms:get_flash 'submit_success' />" />
<cms:if submit_success >
<h4>Saved.</h4>
</cms:if>
<cms:form
masterpage=k_template_name
mode='edit'
enctype='multipart/form-data'
method='post'
anchor='0'
>
<cms:if k_success >
<cms:db_persist_form
_invalidate_cache='0'
/>
<cms:if k_success >
<cms:set_flash name='submit_success' value='1' />
<cms:redirect k_page_link />
</cms:if>
</cms:if>
<cms:if k_error >
<div class="error">
<cms:each k_error >
<br><cms:show item />
</cms:each>
</div>
</cms:if>
<!--
bound inputs can be placed here
-->
<cms:input type='submit' name='submit' value='Submit' />
</cms:form>
As you can see, it is a regular DBF. We have attached it to the template it is placed in and at this point there are no type 'bound' inputs within it. The intention of this post is to see how we can explicitly set the data for a repeatable-region, so the action we are interested in would lie almost entirely in the <cms:db_persist_form /> statement within the success block of the form.
To see that clearly, empty our repeatable-region by deleting all rows currently in it and save.
Now modify our form to make it as follows -
- Code: Select all
..
<cms:if k_success >
<cms:capture into='my_data' is_json='1' >
[
{
"my_text":"One",
"my_checkbox":"Newspaper",
"my_dropdown":"-"
},
{
"my_text":"Two",
"my_checkbox":"Website",
"my_dropdown":"1"
},
{
"my_text":"Three",
"my_checkbox":"Newspaper|Phone Book",
"my_dropdown":"2"
}
]
</cms:capture >
<cms:db_persist_form
_invalidate_cache='0'
my_repeatable=my_data
/>
..
As you can see, we have made two changes -
1. Added a <cms:capture> block just above the <cms:db_persist_form> statement.
This block sets a variable named 'my_data' using the same JSON that we saved earlier.
2. Explicitly set my_repeatable=my_data within <cms:db_persist_form> (which is what the entire exercise is all about).
Submit the form and in the admin-panel you should see that our three original rows have been restored using the explicit data we passed to <cms:db_persist_form>.
Of course, in a real-world scenario, we'll be crafting our own JSON instead of using hard-coded data as in our example above but this should serve as a clear example of what needs to be done.
Continuing using the hard-coded JSON data - press submit once again. What do you think will the repeatable-region (which at this point has three rows) end up containing now? Take a look and you'll find that no matter how many times you submit the form, the repeatable-regions always ends up having the same three rows.
That is because the value we pass on to <cms:db_persist_form> completely overwrites whatever existing data is contained in the repeatable-region.
If your use-case dictates that each form submission preserves the existing data and appends the new data as a new row, you can use the following method -
- Code: Select all
<cms:if k_success >
<cms:capture into='my_data' is_json='1'>
<cms:show_repeatable 'my_repeatable' as_json='1' />
</cms:capture >
<cms:capture into='my_data.' is_json='1'>
{
"my_text":"Three",
"my_checkbox":"Newspaper|Phone Book",
"my_dropdown":"2"
}
</cms:capture >
<cms:db_persist_form
_invalidate_cache='0'
my_repeatable=my_data
/>
..
The code requires some explanation.
The first <cms:capture> sets our 'my_data' variable by reading in the current data from the repeatable-region.
So we begin with 'my_data' as an array containing all the current rows of the region.
Look carefully at the second <cms:capture> block - the variable it is setting has a trailing '.' (dot).
As explained in the docs on "Multi-value variables (or Arrays)" viewtopic.php?f=5&t=10892, the dot serves to add a new row (unnamed key, actually) to an existing array.
So basically the second <cms:capture> appends our (hardcoded at this point) data as a new row to the existing rows of the repeatable-region.
Try it and you'll see that each submission adds a new row while preserving the existing rows.
Up until this point, we have been working with hard-coded JSON.
It is finally time to use submitted data to create JSON dynamically.
Add three inputs to the form as follows -
- Code: Select all
<!--
bound inputs can be placed here
-->
<cms:input name='my_text2' type='text' /><br>
<cms:input name='my_checkbox2' type='checkbox' opt_values='Newspaper || Website || Phone Book' /><br>
<cms:input name='my_dropdown2' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' /><br>
<cms:input type='submit' name='submit' value='Submit' />
..
Please notice that these are not 'bound' inputs. Just plain cms:inputs with parameters that exactly match those used by the constituent editable regions used within our repeatable-region.
As we know, upon successful form submission, Couch will make available the contents of these inputs as variables with 'frm_' prepended to their names. We'll now make use of those variables to make our JSON dynamic as follows -
- Code: Select all
<cms:capture into='my_data.' is_json='1'>
{
"my_text" : "<cms:show frm_my_text2 />",
"my_checkbox" : "<cms:show frm_my_checkbox2 />",
"my_dropdown" : "<cms:show frm_my_dropdown2 />"
}
</cms:capture >
The code above should serve as the final template for you to generate your JSON (names of fields in quotes followed by their values in quotes and no comma after the last field) but there is a potential problem that needs to be addressed here before we call it quits.
JSON format is pretty unforgiving and explicitly forbids several character in its input. According to http://json.org/, following characters must be escaped before being used in JSON strings
quotation mark "
forward slash /
back slash \
new line n
carriage return r
tab t
To see what I mean, try inputting hello \ world in the text box. The resulting row will come up blank as the generated JSON is considered malformed.
To prevent that from happening, we'll make a final change to our code -
- Code: Select all
<cms:capture into='my_data.' is_json='1'>
{
"my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
"my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
"my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
}
</cms:capture >
Instead of quotes around the values, we use <cms:escape_json></cms:escape_json> instead. This tag takes care of escaping all JSON problem characters (and also surrounds the resulting string in quotes - which is why we are no longer using quotes ourselves).
And that is it. Final code stands as follows -
- Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template>
<cms:repeatable name='my_repeatable' order='-2'>
<cms:editable name='my_text' type='text' />
<cms:editable name='my_checkbox' type='checkbox' opt_values='Newspaper || Website || Phone Book' />
<cms:editable name='my_dropdown' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' />
</cms:repeatable>
</cms:template>
<cms:set submit_success="<cms:get_flash 'submit_success' />" />
<cms:if submit_success >
<h4>Saved.</h4>
</cms:if>
<cms:form
masterpage=k_template_name
mode='edit'
enctype='multipart/form-data'
method='post'
anchor='0'
>
<cms:if k_success >
<cms:capture into='my_data' is_json='1'>
<cms:show_repeatable 'my_repeatable' as_json='1' />
</cms:capture >
<cms:capture into='my_data.' is_json='1'>
{
"my_text" : <cms:escape_json><cms:show frm_my_text2 /></cms:escape_json>,
"my_checkbox" : <cms:escape_json><cms:show frm_my_checkbox2 /></cms:escape_json>,
"my_dropdown" : <cms:escape_json><cms:show frm_my_dropdown2 /></cms:escape_json>
}
</cms:capture >
<cms:db_persist_form
_invalidate_cache='0'
my_repeatable=my_data
/>
<cms:if k_success >
<cms:set_flash name='submit_success' value='1' />
<cms:redirect k_page_link />
</cms:if>
</cms:if>
<cms:if k_error >
<div class="error">
<cms:each k_error >
<br><cms:show item />
</cms:each>
</div>
</cms:if>
<!--
bound inputs can be placed here
-->
<cms:input name='my_text2' type='text' /><br>
<cms:input name='my_checkbox2' type='checkbox' opt_values='Newspaper || Website || Phone Book' /><br>
<cms:input name='my_dropdown2' type='dropdown' opt_values='Make a selection=- | Yes=2 | No=1' /><br>
<cms:input type='submit' name='submit' value='Submit' />
</cms:form>
<?php COUCH::invoke(); ?>
Hope this helps.