Hey Seth,
Thanks for your message. There are some courses and activities in Skuid Skool that deal with mass actions, I listed them below. I reported the dead link to the documentation team so we can get that fixed soon. Here’s the correct link: Context — Skuid v15.3.7 Documentation
Mass actions can be tricky! Context is also tricky (sigh). As you discovered with your toast action, the context that governs merge syntax (e.g. what shows up when you say {{Field}}), doesn’t always match the action context (i.e. which rows a mass action runs for) - like you found when the mass action to show a toast message. I’m not sure if it’s expected for mass actions to show the toast multiple times, one for each row, or just once, but I’ll check with the team. I think it is expected that the merge syntax would default to the first row in the model for mass actions, even though that feels weird.
Can you share more about what you’re trying to do with your mass action? From the description it sounds like you have a table on Model A and want to activate and set model conditions on another model (Model , query Model B, make updates based on the values of the selected rows in Model A, and then save Model B field actions. Is that correct? Are you trying to update the condition values just once before running the sub-actions for each row, or once for each row?
Based on the complexity of this process, it does sound like you may need to use Javascript. You can use Javascript for v2 mass actions as far as I know but it may be different than in v1 (this post)
Can you share more about your use case? That way I can check with the team and see what methods people recommend. Is it what you describe in your post here? Sounds like Model A, that the table is on, is an aggregate model, and you want your mass action to query Model B (a basic model) for the selected rows so that you can update them? Do you want to update them all to the same value, or will the value vary by row?
Skuid Skool Courses
Configure Actions in Skuid (Level 1) - introduction to actions that includes a basic activity setting up global, row, and mass actions. Although your use case sounds more complex than this, this is a good place to start so I do recommend this course if you haven’t taken it yet.
Magical World of Merge Syntax (Level 2) does have a section about context in Row Merge Variables but this is more about how context for merge syntax works in general, not so helpful for determining context for mass actions
Thanks for your feedback about the docs and Skuid Skool! We are continually working on these to make them as helpful as possible.
Hi Anna,
Thank you for your reply. I had bumped into that Context link, at least the 15.3.5 version.
The Skuid Skool courses were somewhat helpful. The Merge Syntax in particular was interesting, though not for the issue at hand.
I’ve yet to find an full explanation of Mass-Actions actions. Are the actions:
- Case 1 - ‘Called/run’ for each selected row in the table in sequence?
or
- Case 2 - The actions run ONCE for the collection of selected rows, and some of the possible action-types can operate on the selected rows collectively. (ie. Update one field the same way on every selected row. or Delete all the selected rows)
From Configure Actions in Skuid (Level 1) lesson, it would seem mass-action is Case 2. The actions are run just once for the collection of selected rows. That’s consistent with the Toast only popping up once.
If that’s the case, then the Mass-Action is really a just a button which can run actions where SOME TYPES of actions can act on the collection of selected rows.
…
What I’m working on is building a ‘ProjectCampaign’ ‘screen’ where we can select NPSP allocations to add to the ProjectCampaign. (I added a campaign lookup in the NSPS allocation object.)
Model-A is a Campaign model.
Model-B is an AGGREGATE model on the NPSP Allocations object. (Filtered by GAU Fund and a date range), grouped by the text of the Acknowledgement field.
Model-C is a non-aggregate model of the NSPS allocations with the added criteria of the Acknowledgment text field. (The -Allocations to import- table on screen is just for development/debugging.) - This is historical/legacy data where the text of the acknowledgement can vary as it’s typed in/edited when posted. That’s why I need to be able to select specific groups to import. Also a fund might have donation for multiple projects. (In this sample, it’s a ‘where needed most’ catch-all fund with many different projects)
The Import Allocations table uses the aggregate model of the NPSP allocations, to display the groups of allocations available to import. (I don’t have test data, just a subset of real data, so I obscured the specifics)
The intent was to have the user select specific aggregate rows to import, and use a Mass-Action to sequentially import the underlying Allocation records. (Update ‘ProjectCampaign’ fields on those allocation records, typically they’d be dozens of allocations but might be thousands for some projects. )
So my mass-action (with the understanding that the actions were run once for each selected row) was going to:
- Lock the UI
- Show Toast for the text of the Acknowledgement that’s being imported
- Update the conditions on AllocationsToImport model (NOT AGGREGATE) with the matching fund/closedate range/acknowldgement conditions.
- Query the model
- Drop to Javascript to loop thru the model and update the records (or use an action to do that, but hadn’t gotten that far to see if there was an action to bulk update all the model records)
- At some point I also need to add CampaignMembers for the accounts that made the donations. (Allocation->Donation Opportunity->Account) I’m expecting that will need to be batch-apex for dealing with duplicate CampaignMembers.
- Save the model changes.
- Loop back to the next selected row.
- Unblock UI
It would seem Mass-Action can’t handle that.
The final-end-result is a campaign where we can see WHO(accounts) gave What(allocations) to specific projects and use normal SF functionality to contact the donors with progress on the projects. (Email/Letters etc)
…
Regarding the full Javascript option (I was fully prepared and expecting to do it in JS), I had found the conversation you linked to about the v1 JS solution. As I understand the answer to it, the answer was Can’t get there from here. NO Javascript functionality in V2 to get access to a list of selected rows. I replied to that thread, but nobody chimed in yet to say otherwise.
I spent a couple hours digging around in the chrome debugger but couldn’t find the selected list anywhere. I was expecting to be able to get a hold of the rows (model or table) and loop thru them and find a Boolean field indicating the row was selected, but no joy. Couldn’t find anything. Also guessing that the mess-with-the-guts solution would be fragile when Skuid makes changes.
My current workaround, (which I still haven’t finished) is row action.
That’ll work for development, but I don’t relish having the users use it in production when there might be dozens of rows to import per project.
Thanks
Hey Anna,
Any further thoughts? You had asked for my use case…
So is javascript really not an option to get access to the selected rows?
Thanks,
Seth
Hey Seth,
I did a quick read through, and I figured I could provide an example page that includes a couple options for using mass actions with each row in context. Like you commented, thinking of mass actions as something that runs once on the selected rows is pretty accurate. What happens is the context passed into the given action is the selected rows, which works well with actions that have a property for Rows to use
, like Create or Update rows. That being said, you can get each row in context via a ui-only field and actions or javascript. The XML below is all ui-Only so you should be able to test it out.
Ui-Only field & model action: This works well if you are running simple actions that don’t have shared resources (ex more complicated if you need to query a model based on the selected rows since you need a way to concatenate the selected rows’ ids for activating & setting the condition). In the example, I want to create a record in the Odds model if the selected row is an odd number, and Evens model if it’s even. Since it needs a branch for that determination, I can’t just do a Create row action. What I do is update a ui-only field in the model, and have an action that fires when that field is updated. This means if I select 5 rows and click the button, it updates all 5 rows in a single action, which causes 5 individual model actions to fire, each with 1 of the 5 rows in context.
Javascript: I didn’t do a fully fledged example here, but if you check the console after clicking the mass action you’ll see the logged rows
property only includes the selected rows. This means you can use params.rows
to access an array of the rows the user selected.
Let me know if this helps at all, the options should make more sense with the example page.
<skuid__page unsavedchangeswarning="yes" personalizationmode="server" showsidebar="false" showheader="false" title="Mass Action Example">
<models>
<model id="Numbers" limit="20" query="true" createrowifnonefound="false" datasource="Ui-Only">
<fields>
<field id="rownum" displaytype="FORMULA" length="255" label="rownum" ogdisplaytype="TEXT" precision="9" scale="0" readonly="true" returntype="DOUBLE">
<formula>{{index}}</formula>
</field>
<field id="initiateMassAction" displaytype="DATETIME" length="255" label="initiateMassAction" ogdisplaytype="TEXT"/>
</fields>
<conditions/>
<actions>
<action>
<actions>
<action type="branch" whenfinished="stop" model="Numbers">
<formula>{{rownum}} % 2 != 1</formula>
<iftrueactions>
<action type="createRow" model="Evens" appendorprepend="append" defaultmodefornewitems="read" affectedrows="context">
<defaults>
<default valuesource="fieldvalue" field="rownum" enclosevalueinquotes="false" value="{{rownum}}"/>
</defaults>
</action>
</iftrueactions>
</action>
<action type="createRow" model="Odds" appendorprepend="append" defaultmodefornewitems="read" affectedrows="context">
<defaults>
<default valuesource="fieldvalue" field="rownum" enclosevalueinquotes="false" value="{{rownum}}"/>
</defaults>
</action>
</actions>
<events>
<event>row.updated</event>
</events>
<fields>
<field>initiateMassAction</field>
</fields>
</action>
</actions>
</model>
<model id="Evens" limit="20" query="true" createrowifnonefound="false" datasource="Ui-Only">
<fields>
<field id="rownum" displaytype="DOUBLE" length="255" label="rownum" ogdisplaytype="TEXT" precision="9" scale="0" readonly="false" returntype="DOUBLE">
<formula>{{index}}</formula>
</field>
</fields>
<conditions/>
<actions/>
</model>
<model id="Odds" limit="20" query="true" createrowifnonefound="false" datasource="Ui-Only">
<fields>
<field id="rownum" displaytype="DOUBLE" length="255" label="rownum" ogdisplaytype="TEXT" precision="9" scale="0" readonly="false" returntype="DOUBLE">
<formula>{{index}}</formula>
</field>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<skuid__grid uniqueid="sk-iX3-21800" flexDirection="row" justifyContent="flex-start" alignItems="flex-start" columnGutter="4">
<divisions>
<division minWidth="100px" ratio="1">
<components>
<skuid__table allowColumnFreezing="dragDrop" model="Numbers" uniqueid="sk-iX2-20959" mode="read" showSaveCancel="false" title="Numbers">
<fields>
<field id="rownum" horizontalAlignment="right" uniqueid="fi-iX2-21423"/>
</fields>
<filtering enableSearch="false"/>
<actions/>
<rowActions/>
<massActions>
<action type="multi" label="Create Rows">
<actions>
<action type="custom" snippet="logParams"/>
<action type="updateRow" fieldmodel="Numbers" affectedrows="context">
<updates>
<update valuesource="fieldvalue" field="initiateMassAction" enclosevalueinquotes="false" value="NOW"/>
</updates>
</action>
</actions>
</action>
</massActions>
<exportProperties useTableColumns="true" sanitizeRecords="true"/>
<sorting enable="false"/>
</skuid__table>
</components>
</division>
<division alignSelf="auto" minWidth="100px" ratio="1">
<components>
<skuid__table allowColumnFreezing="dragDrop" model="Evens" uniqueid="sk-iXG-32902" mode="read" showSaveCancel="false" title="Evens">
<fields>
<field id="rownum" horizontalAlignment="right" uniqueid="sk-iXG-32903"/>
</fields>
<filtering enableSearch="false" searchMethod="client"/>
<actions/>
<rowActions/>
<massActions/>
<exportProperties useTableColumns="true" sanitizeRecords="true"/>
<sorting enable="false"/>
</skuid__table>
</components>
</division>
<division alignSelf="auto" minWidth="100px" ratio="1">
<components>
<skuid__table allowColumnFreezing="dragDrop" model="Odds" uniqueid="sk-iXE-30486" mode="read" showSaveCancel="false" title="Odds">
<fields>
<field id="rownum" horizontalAlignment="right" uniqueid="sk-iXE-30487"/>
</fields>
<filtering enableSearch="false" searchMethod="client"/>
<actions/>
<rowActions/>
<massActions/>
<exportProperties useTableColumns="true" sanitizeRecords="true"/>
<sorting enable="false"/>
</skuid__table>
</components>
</division>
</divisions>
</skuid__grid>
</components>
<resources>
<labels/>
<javascript>
<jsitem location="inlinesnippet" name="logParams" cachelocation="false">const params = arguments 0];
console.log(params);</jsitem>
</javascript>
<css/>
<actionsequences>
<actionsequence id="67b56de6-04e6-485f-b7f0-d260ec3d6889" label="Rendered" type="event-triggered" event-scope="component" event-name="page.rendered">
<description/>
<actions>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="action-sequence" action-sequence-id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529"/>
<action type="save">
<models>
<model>Numbers</model>
</models>
</action>
</actions>
</actionsequence>
<actionsequence id="f5fcabf3-a168-49aa-9a68-e61c7b9cb529" label="Create number row" type="reusable">
<description/>
<actions>
<action type="createRow" model="Numbers" appendorprepend="append" defaultmodefornewitems="read" affectedrows="context"/>
</actions>
</actionsequence>
</actionsequences>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</skuid__page>
Hi Matt,
Thanks for the sample. It was helpful.
The params.rows I think is the real solution. That other community answer about JS can Mass Actions, should probably be updated with the params.rows property info. I’d reply myself, but if someone from Skuid replied it would be more authoritative…
Perhaps at somepoint the JS docs could be updated. A v2 version on Table Component: Custom Mass Actions — Skuid v15.3.7.0 Documentation
My use case for this instance got fully in the JS realm, as I’m adding campaign members from NPSP allocations (Importing them to a ‘Project Campaign’), and as such I need to manage duplicate campaign members.
So I have to load them in an ‘existing campaign members’ model, then check that for the donors I wish to add as campaign members… fun times.
Currently, I ended up making a flow trigger on the allocation object to check if donor is already a campaign member and if not, add them to the campaign.
Thanks,
Seth