Important announcements from CouchCMS team
22 posts Page 2 of 3
Adding a few moments to previous post. Arrays declaration. Arrays are in [] , Objects are in {}

Create a first empty array
Code: Select all
<cms:set person = '[]' scope='global' is_json='1' />

Add to it one object, consisting of a key 'First Name' and a value 'Jane'.
Code: Select all
<cms:set person. = '{"First Name" : "Jane"}' scope='global' is_json='1' />

Alternative way to do the above 2 operations in one go is:
Code: Select all
<cms:set person = '[{"First Name" : "Jane"}]' scope='global' is_json='1' />
If anyone reads this topic, here is a sample of converting output of listing tag (query, pages, folders etc) to JSON and JSON to ul-li list. viewtopic.php?f=2&t=10262&p=28787#p28787
KK wrote: recursively iterating through all levels


I was curious and came up with a solution, using a snippet to make things easy.

Sample test 4-level deep array will be created by php just to test things.
Code: Select all
<cms:php>
    $a = array_fill(0, 2, array_fill(0, 2, array_fill(0, 3, array_fill(0, 2, 0))));
    global $CTX;
    $CTX->set('test_array', $a, 'global');
</cms:php>


Then there will be a snippet file, let's say "loop.html", placed in /snippets as usual and with following code (calling itself!):
Code: Select all
<cms:ignore>
/*
    It loops and calls itself to build a multi-dimensional ul-li out of array.
   
    Sample to call this snippet:
   
    <cms:set item = some_array />
    <cms:embed 'loop.html'/> 

*/
</cms:ignore>


<cms:if "<cms:not limit_array_levels />">
    <cms:set limit_array_levels = '10' scope='global'/><cms:ignore>A reasonable limit of nested levels</cms:ignore>
</cms:if>

<ul>
<cms:each item>
    <cms:if "<cms:is_array item />">
    <li><kbd><cms:show key /></kbd>
        <cms:if startcount lt limit_array_levels >
        <cms:embed 'loop.html' startcount="<cms:add startcount '1' />" ></cms:embed>
        <cms:else />
            : Array (<cms:array_count item />)
        </cms:if>
    </li>
    <cms:else />
    <li><kbd><cms:show key /></kbd> : <cms:show item /></li>
    </cms:if>                       
</cms:each>
</ul>


Now with this snippet, all we need to do to build an unordered list ul-li is to call this snippet. For our experimental "test_array" it looks like this:
Code: Select all
<cms:set item = test_array />
<cms:embed 'loop.html' />


Browser shows it as following:
2018-03-02-001.png
2018-03-02-001.png (5.66 KiB) Viewed 5371 times


Most practical use-case for me is to pleasantly navigate through dataTable's arguments that it sends over ajax to backend:
2018-03-02-002.png
2018-03-02-002.png (9.19 KiB) Viewed 5371 times


There is a built-in option to control how many levels are visible.
Hopefully this little trick with recursion helps me many times.
Json pretty print tag viewtopic.php?f=8&t=11466
Something I can't figure out here. I'd like to be able to show and set values within an array in an 'each', setting the value relating to the 'item' in the loop.

So that I know the basics are there, I'm using code from examples back in the early part of this thread.

Code: Select all
<cms:set climate='[]' is_json='1' />
<cms:set climate. = 'unknown' />
<cms:set climate. = 'uncertain' />
<cms:set climate. = 'finicky' />

<cms:show climate as_json='1' /><br>
<cms:show climate.1 /><br>



So far so good: this has the expected output. But now I want to address the array within an 'each' loop:

Code: Select all
<cms:set loop_values = '0|1|2' />
<cms:each loop_values sep='|' >
    <cms:get climate."<cms:show item/>" /><br>
</cms:each>


...and now I get an error "ERROR! ATTRIB_NAME: Invalid char """ "

Is it possible to get and set values within an array in this sort of way? And if so what am I missing?
You were very close :)
This is the correct syntax -
Code: Select all
<cms:set loop_values = '0|1|2' />
<cms:each loop_values sep='|' >
    <cms:get "climate.<cms:show item />" /><br>
</cms:each>

Hope this helps.
Thanks, KK - I thought I'd tried that, but obviously I hadn't!

Probably useful to have it here for people to see in future, though - I like to think people can benefit from my lack of understanding sometimes!
Actually, one more thing: this doesn't seem to work with 'set'. Is a there a way of using set in this context, to update an array within an 'each' loop?

Code: Select all
<cms:set "climate.<cms:show item />" = 'wibble'/> 
causes an error: ERROR! ATTRIB_NAME: Invalid char "="
<cms:set> is the counterpart of <cms:show> and works with variable names that are known beforehand.

For use-cases, like yours, where we need to craft variable names on the fly -
1. For retrieving data, instead of <cms:show>, we use <cms:get>.
2. For setting data, instead of <cms:set>, we use <cms:put>

You have already seen the use of <cms:get>. Following is an example of <cms:put> where we manipulate only certain items of an array -
Code: Select all
<cms:set climate='[]' is_json='1' />
<cms:set climate. = 'unknown' />
<cms:set climate. = 'uncertain' />
<cms:set climate. = 'finicky' />
Before: <cms:show climate as_json='1' /><br>

<cms:set loop_values = '0|1|2' />
<cms:each loop_values sep='|' >
    <cms:if item eq '0' || item eq '2'>
        <cms:put "climate.<cms:show item />" 'wibble' scope='parent' /><br>
    </cms:if>
</cms:each>
After: <cms:show climate as_json='1' /><br>

Output:
Code: Select all
Before: ["unknown","uncertain","finicky"]

After: ["wibble","uncertain","wibble"]

Please do note the use of scope='parent' in the snippet above.
It was necessitated because the <cms:put> statement was being used from within a <cms:each> block.

The default behavior of all setters is to put variables in 'local' scope - in the case above since we are using <cms:put> from within <cms:each> block that itself has a scope (i.e. can hold variables that are not visible beyond the block) it would have set a variable within <cms:each> itself instead of targeting the one we want which happens to lie outside <cms:each>

By specifying the scope as 'parent', we tell <cms:put> that we wish to set a variable that is already present somewhere in the parent scope (i.e. somewhere above <cms:each>). This way we were able to change the existing array.

Hope this helps.
Thanks again, KK - very clear as always!

Since I'm going to be using this several times in the page, I've taken it out into functions:

1. Update the item:
Code: Select all
<cms:func 'updateclimateitem'  value=''>
    <cms:put "climate.<cms:show item />" "<cms:show value />" scope="parent" />
</cms:func>


2. Append further text to the item (like cms:concat) :
Code: Select all
<cms:func 'concatclimateitem'  value=''>
    <cms:set element= "<cms:get "climate.<cms:show item />" />" />
    <cms:set element = "<cms:concat element value />" />
    <cms:put "climate.<cms:show item />" "<cms:show element />" scope="parent" />
</cms:func>


Hope this may be useful to other people.
22 posts Page 2 of 3