Nepz,
Try ‘re-rendering’ the Queue component after you query the Opportunity Model. Use this in a snippet:
skuid.component.getById('sk-1wkWJ9-158').render();
Thanks,
Bill
Thanks Bill, this is probably the best approach.
For Nepz, and other curious folks: to make this work, I added the snippet as an inline (snippet), then I added a model action that fired off the snippet any time a row on the model was updated. Running the snippet from the click interaction in the queue didn’t seem to work.
Here is the XML with this snippet employed:
<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>
<action>
<actions>
<action type="custom" snippet="rerender"/>
</actions>
<events>
<event>row.updated</event>
</events>
</action>
</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"/>
</interaction>
</interactions>
<searchfields/>
</queue>
</components>
<resources>
<labels/>
<javascript>
<jsitem location="inlinesnippet" name="rerender" cachelocation="false">
skuid.component.getById('sk-1wkWJ9-158').render(); </jsitem>
</javascript>
<css/>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</skuidpage>
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>
Thanks Bill & Barry for your solutions. I was trying to file a bug/improvement for SKUID to take action.
I would expect to hook watcher for each merge syntax encountered while compiling pageXML. I know sometimes, framework does’t do that for performance issues…but, still want to highlight these gaps to SKUID…
Hi Nepz -
Unfortunately, I believe Skuid will tell us (and rightfully so) that currently, object properties aren’t automatically “watched.” That said, like you, I think it’s something that would be beneficial at least in certain scenarios like “data.length”.
An alternative to this would be to expose a formula like “COUNT.” This is something I’ve done in my own code but unfortunately, Skuid doesn’t offer it out of the box and currently there is no way to “share” formulas. Fortunately, that is going to change soon hopefully - See https://community.skuid.com/t/custom-action-support?utm_source=notification&utm_mediu… and https://community.skuid.com/t/add-support-for-registering-custom-formulas.
In the meantime, I’d recommend that you to create a new issue marked as an “idea” asking for a way to track “record count” either through handling updates to data.length or via a formula. I think this would be a great addition to Skuid and I’d vote for it