Hello All -
To start with, let’s baseline what’s happening here and help Nepz (and others who might be reading this), understand why he’s not encountering the behavior he is expecting. Gaining an understanding of how skuid works is critical to using it effectively.
At a surface level, one would expect that merge syntax like Nepz has within a header text, search text, section label, item label, etc. would automatically updated as model data changes. In the majority of cases, it does. There are known issues (some very recently addressed including this one for queue) that are targeted at ensuring merge syntax updates as one would expect.
In this specific case, even though queue headers and items now properly update as model data changes, the queue text is still not changing. Let’s understand why…
The reason that it does not in this case is because the merge syntax is applied to a property of the model object itself, not a field (e.g. $Model.Opportunity.data.length as opposed to $Model.Opportunity.data.0.Name. When skuid processes merge syntax, it hooks up listeners to detect changes in field values. Unfortunately, it doesn’t hook a listener to detect changes in model properties like “data.length.” This is ultimately why the text is not updating as the model changes.
So, how can we do this? Let’s look at the options presented thus far:
- Force Render to occur in click interaction - This, in theory, should work. However, there are things that should be known about calling “render” before its used. Forcing a render to occur should be one of the last options taken. Unless you fully understand how/when you are calling render, calling render could lead to unintended results.
a) For example, let’s say that you call render from a model action such as “row.updated.” If you explicitly call render on a component that is currently conditionally rendered “out”, it will now appear even though it’s conditions say it should not. This is where the “conditionallyRender” method comes in. Bills suggestion to put the render in the click interaction is “safe” because the queue will already be rendered. Still, it’s important to understand what render does and how to “safely” use it. In short, it’s best to avoid render unless absolutely necessary (which it will be as you will see below) and instead, let Skuid’s runtime take care of rendering.
b) Also, rendering a component will “reset” it - Let’s say you have a table and the user is on page 5, then you force a render - the user will end up back at page 1. This is unlikely to make the user happy.
- Force a render to occur in model row.updated - When a row is removed from a model, there is no event exposed on the model to trigger an action sequence. Row.Updated will not be triggered when “Remove Row” is called. It’s been asked in the past (see here) to expose events for removing rows but unfortunately, there is no declarative way to trigger actions for this yet
All this talk Barry, just tell me, how do we do it then?
Glad you asked! Since Skuid won’t automatically track $Model.Opportunity.data.length but it will track a field in a row via data.#.fieldname, we can use a tracking model with a UI-Only field to give it what it needs. Here’s how:
- Create a UI only model called “OpportunityTracker”
- Add a field called “OpportunityCount” as a number with zero decimal places
- Update the merge syntax to use {{$Model.OpportunityTracker.data.0.OpportunityCount}}
- Call a snippet in the click interaction that will update the OpportunityCount
- In page load handler, update the tracker count to set our initial value (default values on model fields don’t apply merge syntax so we must use javascript)
Presto - we’re done right? Well, no, sorry.
I put together a sample page and unfortunately, it uncovered a Skuid bug specific to updating the search text. The approach above works just fine for the queue header text but not the search text. I’ve logged the issue here.
Nepz - If you can live with not having search text updated, the sample page in the issue will give you what you need to solve your situation.
Well that’s a big bummer, guess we’re stuck now, huh?
Haha, don’t fear my skuid friend, we have options while we wait for the fix to that issue.
Let’s circle back to Bill’s original suggestion of forcing a render. I know, I said avoid it unless you have to but this is a situation where, as long as we fully understand how/when to use, we can use it.
Calling render on the queue in the click interaction is the approach we need to take. Unfortunately, we’ll encounter another Skuid bug where the header section of the queue gets duplicated.
Drats! Seriously? Yeah, sorry. I’ve filed the bug here.
Ok, so now we must really be stuck, right? I mean, two bugs and one super long post explaining all this only to find out it can’t be done?
Fortunately, no. I’m not one to give up on a problem The solution (for now, until the other issues are fixed) is to use a variant of Bills idea.
The final solution to ensure everything is updated while we wait for those fixes is to “unrender” the queue first, then “render” it. Keep in mind that this will “reset” the queue (as described above) and is only intended to be a stop-gap until the other two issues above are addressed.
Here’s a working sample. Hope this helps!
<skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="true" useviewportmeta="true" showheader="true"> <models>
<model id="Opportunity" limit="20" query="true" createrowifnonefound="false" datasource="salesforce" type="" sobject="Opportunity">
<fields>
<field id="Name"/>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<queue model="Opportunity" tagrendertype="template" searchbox="true" tokenizesearch="true" showsearchbydefault="true" uniqueid="sk-1wkWJ9-158" searchmethod="server" searchplaceholdertext="Search {{$Model.Opportunity.data.length}} Items" emptysearchbehavior="query" title="List of {{$Model.Opportunity.data.length}} Opportunities">
<rendertemplate>{{Name}}</rendertemplate>
<interactions>
<interaction type="tap">
<action type="abandonRows" querystring="id={{Id}}" model="Opportunity" affectedrows="context"/>
<action type="custom" snippet="renderQueue"/>
</interaction>
</interactions>
<searchfields/>
</queue>
</components>
<resources>
<labels/>
<javascript>
<jsitem location="inlinesnippet" name="renderQueue" cachelocation="false">var params = arguments[0]
, $ = skuid.$
, queue = skuid.$C('sk-1wkWJ9-158');
queue.unrender();
queue.render();</jsitem>
</javascript>
<css/>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</skuidpage>