Asset listing shadow assets and data records from metadata

Matrix Version: 6.75.0

Not sure if this is entirely possible, but we’re coming up short on ways to make this work. So far, we’ve got like 95% of the way there, but can’t seem to get over the line. This is long, a little in-depth, and a very long shot, apologies :sweat_smile:

We have a JSON data source that’s pulling in data from Funnelback, and we can see the data source record sets underneath with all the data. Additionally, we have a folder full of Data Records (all we need from these is the name of the record).

In the metadata of a page, we have an asset picker which is locked to the folder where our data records and source is located (it’s not locked to any data type). In this picker, we want to be able to mix and match records and record sets, and that part seems to be working nicely. The idea is that Records become a heading / separator and the Sets become an accordion heading/content.

This is being added to a custom paint layout we have set up. In the past, we’ve used the following code structure for printing things out with asset listings without issue, but I guess when shadow assets some into play it’s a little different?

print('%' + 'globals_asset_contents_raw:123456^with_get:root=%asset_metadata_assetListing%' + '%');

123456 is our asset listing, and we’ve set up the asset listing to use an array of asset IDs and the dynamic root node, which should pull from our metadata field.

“There are no results” is all we get.

The listing itself is only set to list Data Source Record Set and Data Record assets, there are no status restrictions and root node is the same folder that our metadata field is locked down to. We’ve tried specifying display formats for both assets types, so we know there’s definitely a styling difference.

If I go to the asset listing page directly, though, and manually pass ?root in the URL (?root=317867,317102:3), I do get an output, but it’s only for the Data Record, nothing for the record set.

The last thing we were playing around with, was using JS and for looping through the metadata field manually. We did get some traction here, in that we found away to access the data of the shadow assets and display the content, but our problem then becomes separating the Records and Sets.

We couldn’t figure out how to work through the order of operations to do something like checking the asset type, and displaying different styling. I’ll drop some code below that we were testing out. There are some different variations of this, including saving the globals into a js variable and trying to run a check from there, but yeah I think parsing order is tripping us up here.

SSJS code we were testing out
for(let i=0; i < modules1.length; i++) {
    print( '%' + `begin_globals_asset_assetid:${modules1[i]}^as_asset:asset_type^eq:Data Record` + `%
				<p>` + '%' + `globals_asset_assetid:${modules1[i]}^as_asset:asset_name` + '%' + `</p>
		` + '%' + `else_globals_asset_assetid${modules1[i]}^as_asset:asset_type^eq:Data Source Record Set` + '%' + `
			<h2>
				Record name
			</h2>
			<div>
				Content
			</div>
		` + '%' + `end_globals` + `%`);
}

Some other things I looked into briefly

  • Nesting the asset listing using a nested content container and passing vars that way, but I’m not sure on how to correctly set that up or if it would work.
  • Using a Search page that returns the assets in the metadata field. I’ve configured search pages before, but nothing this specific, and feel like this is probably the longest shot to get working.

Appreciate any and all ideas. Happy to drop any additional code or details if it will help!

If I understand you properly maybe try this because your %begin_globals...% conditions won’t ever run inside SSJS (Matrix parses keywords before your JS executes).

This version keeps everything in SSJS, checks each asset’s type, and outputs the right markup:

<script runat="server">
(() => {
  const modules1 = %asset_metadata_assetListing^json_decode^empty:[]%;
  if (!modules1.length) {
    print('<p>No items selected.</p>');
    return;
  }

  for (let i = 0; i < modules1.length; i++) {
    const id = modules1[i];
    const type = `%globals_asset_type_code:${id}%`;
    const name = `%globals_asset_name:${id}%`;

    if (type === 'data_record') {
      print(`<h3 class="record-heading">${name}</h3>`);
    } else if (type === 'data_source_record_set') {
      print(`
        <div class="accordion-item">
          <button class="accordion-button">${name}</button>
          <div class="accordion-content">%globals_asset_contents_raw:${id}%</div>
        </div>
      `);
    }
  }
})();
</script>

Because the type check happens in JS instead of keyword logic, it correctly handles both Data Records and Data Source Record Sets, including shadow assets from your JSON data source.

1 Like

Hi Nick, this is great, thanks! This does let me list out the contents of both asset types, but the asset_type check is still failing to work, unfortunately. It doesn’t print out anything at all if I have items selected in my metadata field. I had to add another else statement at the end to print the data.

If I check the asset types of both types within there, they both come back as false/no. Seems this is the last hurdle I keep getting stuck at

Hmm maybe try and grab the asset type through the ^as_asset modifier instead

<script runat="server">
(() => {
  const modules1 = %asset_metadata_assetListing^json_decode^empty:[]%;
  if (!modules1.length) {
    print('<p>No items selected.</p>');
    return;
  }

  for (let i = 0; i < modules1.length; i++) {
    const id = modules1[i];
    const type = %globals_asset_assetid:{modules1[i]}^as_asset:asset_type^json_decode%;
    const name = `%globals_asset_name:${id}%`;

    if (type === 'Data Record') {
      print(`<h3 class="record-heading">${name}</h3>`);
    } else if (type === 'Data Source Record Set') {
      print(`
        <div class="accordion-item">
          <button class="accordion-button">${name}</button>
          <div class="accordion-content">%globals_asset_contents_raw:${id}%</div>
        </div>
      `);
    } else {
      // fallback - just print the name
      print(`<p>${name}</p>`);
    }
  }
})();
</script>

The ^as_asset:asset_type runs at parse time for each ID so that should make your type check work correctly.

Just a thought but try changing the condition checks to match the asset_type_code values -
data_record and data_source_record_set

1 Like

Thanks Nick, unfortunately this doesn’t seem to be working either. Played around with keywords and checks a little, but it still just skips over both checks and hits the fallback.

I was in the middle of typing up a “this probably isn’t going to work” response, and I think I’ve found a stupidly simple solution - CSS. Using the type_code as a class, I can hide a few things, alter the styling, and disable pointer events on the data record accordion items and it acts in the same way. Crude, but so far effective.

Going to keep chipping away at an actual solution, if it’s at all possible, but this is something we can maybe run with for now. Appreciate the responses, thank you!

1 Like

I think you might need to restrict to direct links so you can include TYPE_3 link types, otherwise shadow assets won’t be included…

Tried this as well, no joy there either. If that is the case, then I don’t think any solution using asset listings will work for us. We need to pull from both the JSON data source and a separate folder full of data records. The search continues :sweat_smile:

Hi guys, got this solution from Grok which is simillar to Nick’s answer. Worth trying…

<script runat="server">

// 1. Get the raw metadata value (comma-separated list, possibly with :linktype)
var raw = "%asset_metadata_assetListing^trim%";        // ← change "assetListing" to your actual metadata field tag
if (!raw) {
    print("<p>No items selected.</p>");
} else {
    // Split and clean the IDs
    var ids = raw.split(",").map(function(item) {
        return item.trim().replace(/:.*/, "");   // remove :3 or whatever link type
    });

    for (var i = 0; i < ids.length; i++) {
        var assetId = ids[i];

        // These three keywords are 100% reliable and work on shadow assets too
        var typeCode = "%globals_asset_assetid:" + assetId + "^as_asset:asset_type_code%";
        var name     = "%globals_asset_assetid:" + assetId + "^as_asset:asset_name%";
        var contents = "%globals_asset_contents_raw:" + assetId + "%";   // full rendered output (shadow records table, etc.)

        // Normalise type code (JSON data sources can be json_data_source_record_set or just data_source_record_set)
        typeCode = typeCode.toLowerCase();

        if (typeCode === "data_record") {
            // ── Simple heading / separator ──
            print('<h3 class="data-record-heading">' + name + '</h3>');

        } else if (typeCode.indexOf("data_source_record_set") !== -1 || 
                   typeCode.indexOf("json_data_source_record_set") !== -1) {
            // ── Accordion section with all shadow records ──
            print('<div class="accordion-item">');
            print('  <h2 class="accordion-header">');
            print('    <button class="accordion-button" type="button">' + name + '</button>');
            print('  </h2>');
            print('  <div class="accordion-content">');
            print(contents);                       // this prints the whole table/grid of shadow records
            print('  </div>');
            print('</div>');

        } else {
            // Fallback – something unexpected
            print('<p><strong>' + name + '</strong> (type: ' + typeCode + ')</p>');
        }
    }
}
</script>