Populate YAML array from item

I need a little bit of help:
Given this component:

component: oh-button
   action: photos
   - caption: Photo 1
     url: /static/photos/photo1.jpg
   - caption: Photo 2
     url: /static/photos/photo2.jpg
   - caption: Photo 3
     url: /static/photos/photo3.jpg
    - ...

I’d like to populate the url from an item which holds a non determined amount of file names as a csv string, like this:


How can I do that? I also need to add a prefix. For a better understanding I added the code which does not work and omitted the caption attribute. Also a forEach expression does not work,too.

actionPhotos: ='/static/photos/' + items.vKlingelFilenames.state.split(",")

Any ideas?

I think String operations are supported in Expressions so

url: "='/static/photos/' + items['ItemName'].state.split(',')[0]"

should give you the first value in the list. Change the number at the end to go to 2, 3, 4, etc. To make the widget generic and configurable, you could make `‘ItemName’ be a property.

Hey Rich,
thanks. Ok - a workaround if the amount of files is pre-defined.
In my case I do not know how many files are in that directory.

Do you have any other idea?

yaml is a human readable short hand for JSON. The - syntax are array elements. So in reality actionsPhotos is getting set to a JSON array of objects with a caption key and a url key. So:

   - caption: Photo 1
     url: /static/photos/photo1.jpg
   - caption: Photo 2
     url: /static/photos/photo2.jpg
   - caption: Photo 3
     url: /static/photos/photo3.jpg

could just as well be written like this:

actionPhotos: =[{'caption': 'Photo 1', 'url': '/static/photos/photo1.jpg'},{'caption': 'Photo 2', 'url': '/static/photos/photo2.jpg'},{'caption': 'Photo 3', 'url': '/static/photos/photo3.jpg'}]

This gives you a couple of different options. Do you have control over the original list item? By that I mean are you populating this from a rule or external script file? If that’s the case then instead of sending just the list of photos, create the full array object in the rule/script where you have the full power of JS or some other language available to you. If you set the item state to the stringified object then you can just parse that string in the widget.

actionPhotos: =JSON.parse(@@vKlingelFilenames)

If you don’t have that access to the item state then you can probably create the array from the string using split and then use map and an arrow function to map each element in the new array to an object with the correct keys. Of the top of my head I’d say something like this:

actionPhotos: =@@vKlingelFilenames.split(',').map((e,i) => {'caption':`Photo ${i}`,'url':`/static/photos/${e}`})
1 Like

Yes since 3.4 arrow functions are supported so you can map arrays like:

="foo,bar,baz".split(",").map(i => "prefix-" + i)

which will give you:

[ "prefix-foo", "prefix-bar", "prefix-baz" ]


That’s enough for the photo browser, if you don’t need captions, you can just feed it an array of URLs, but you can use @JustinG’s answer to build a more elaborated array.

Thanks @JustinG and @ysc
I adopted your suggestions and found two problems:

  1. As a side note the @-operator does not work in expression tester
    see here:

  2. Justin solutions works great (if I replace the @-operator) in the expression tester
    see here:

    but for some reason it does not work in the widget

actionPhotos: =items.vKlingelFilenames.state.split(',').map((e,i) => {'caption':`Photo ${i}`,'url':`/static/photos/${e}`})

after some more testing: the @-operator works like this:


but .map in combination with @-operater does not work

EDIT2: Hold on, it does not work in the widget editor but when launching a page containing the widget it works perfectly

Yeah, sorry about that. I typed up the example quickly without double checking it. @ and @@ need the item name as a string.

What about if you put the @@ and item name in (...)?

actionPhotos: =(@@'vKlingelFilenames').split(',').map((e,i) => {'caption':`Photo ${i}`,'url':`/static/photos/${e}`})

No. No need to say sorry. I am happy about any direction and it is my job to get the small bits and pieces done.

Then the expression works just fine.

As my expression is getting more and more complex I am creating the whole json string in a seperate rule, as you suggested. This works fine s far. I am struggeling just with the following:

In the epression of the widget I was able to use e.g. split inside the arrow function

(simplified string)

actionPhotos: =items.vKlingelFilenames.state.split(',').map(e => {'caption':e.split('.jpg')[0],'url':`/static/klingel/${e}`})

If I move this expression to a DSL rule then I obviously cannot do the same.

This works just fine:

var jsonPhotos = csvFiles.split('\n').map[e | "{ \"caption\": \"" + e + "\", \"url\": " + "\"/static/klingel/" + e + "\"}"]

but when using .split():

var jsonPhotos = csvFiles.split('\n').map[e | "{ \"caption\": \"" + e.split(".").get(0) + "\", \"url\": " + "\"/static/klingel/" + e + "\"}"]

I get an error message when printing the content of the variable to the log file

if(debug) logInfo(rulename, "jsonString: {}", jsonPhotos)

jsonString: [FAILED toString()]

What do I need to do to use “e” within this lambda function?

:man_shrugging: DSL is not my forte.

I assume that you’re trying to strip the file extension of the file name in the list with e.split(".").get(0). An array conversion is probably overkill. Maybe just try substring instead: e.substring(0,e.indexOf("."))

Unfortunately the same error message. For some reasons these extensions are not allowed.
I’ll ask in the rules subforum where the problem is.

Thanks a lot!