Why doesn't the Chatter Feed refresh when the model reloads?

I have a table that displays a list of records, and details of a selected record below, including the record’s Chatter Feed. When I click an icon the record in the list, a row action sets a condition on a secondary model and refreshes it to show the details of the chosen record in the section below the table. This works fine, except the Chatter Feed doesn’t refresh when I choose another record - it continues to show the first record’s Feed. Also, I notice that I must load a record when the page loads, otherwise the entire component is permanently blank.

I don’t see sufficient documentation on this component to understand this functionality. Why does this component work differently from others? Why doesn’t it refresh when its model refreshes? How do I get it to refresh declaratively on demand? Why does it fail to render properly unless a record exists when the page loads?

Well, I’m pretty much stymied with this. Rob et al, is there any way to get a chatter feed to refresh on demand? I’ve looked at script, includes, master pages, etc. I’m looking for a solution where users can browse through a table and see the feed (and additional details) for the record they choose.

Peter,

Would a Skuid table work for your needs?  You can build a model on the ‘__Feed’ object for your custom object.  Just set a condition where the ParentId of the ‘__Feed’ object = your object Id.  You can also set a condition for Type = ‘TextPost’ to see the comments only.  Since your object is labeled ‘Requirements’, the ‘Feed’ object might be named ‘Requirement__Feed’.

Thanks,

Bill

Hello Peter -

It’s been a couple of years since I used the stock Chatter component, however at the time, I ran in to the same issues you are experiencing. Unfortunately, it doesn’t appear that they have been addressed. In short, you are encountering some Skuid bugs (limitations) with respect to the chatter component which to my knowledge, are still not documented anywhere.

You can fight with the Chatter component as much as you choose to you won’t get very far. At the time, I tried everything I could think of (e.g. forcing unrender/render) but nothing worked. After inspecting the code I found the following primary issues:

  1. In order to work properly, the Chatter component MUST execute on the server and a record MUST be in the model on initial page load. The Chatter component is NOT marked to be JS Createable. This is indicated via the component property isJSCreateable (not officially documented). You can see the effect of this by trying to put a Chatter component in a Popup or Drawer - you can’t :slight_smile: Additionally, it’s marked to “onlyRenderOnce” (onlyRenderOnce component property - not officially documented) which is another indication.

  2. When the page initially loads, if a record exists in the model, skuid will output a combination of javascript & html that will “load” the chatter component. If there is no record when the page loads on the server, “chatter” itself is not even available on the page (via window.chatter).

  3. Inside of the html/javascript that is emitted is the “hook” to maintain the feed. The way this is written the feed it’s subscribed to cannot be changed.

Short of Skuid resolving these issues, the only solution that I can think of to satisfy your use case is to use a template component with an iframe instead of the Chatter component. The downside to this is that using iframes is not fun due to height/width type of issues. Hopefully it will work in your case.

Even if the iframe solution works for you, I would encourage you to continue forward with this issue with the hope that Skuid will either address these bugs/limitations and/or at least document the Chatter component indicating it’s correct use.

Here’s the template that you’ll need (make sure to mark it “Allow HTML”:

<iframe frameborder="0" width="100%" height="800px" src="/apex/skuid__Social?id={{{Id}}}"/>

Here’s a full sample page:

<skuidpage unsavedchangeswarning="yes" personalizationmode="server" useviewportmeta="true" showsidebar="true" showheader="true" tabtooverride="Account">    <models>
        <model id="AccountSummary" limit="100" query="true" createrowifnonefound="false" datasource="salesforce" sobject="Account" type="">
            <fields>
                <field id="Name"/>
                <field id="CreatedDate"/>
            </fields>
            <conditions/>
            <actions/>
        </model>
        <model id="AccountDetails" limit="20" query="false" createrowifnonefound="false" datasource="salesforce" type="" sobject="Account">
            <fields>
                <field id="Name"/>
                <field id="Id"/>
                <field id="Description"/>
            </fields>
            <conditions>
                <condition type="fieldvalue" value="" enclosevalueinquotes="true" field="Id" state="filterableoff" inactive="true" name="Id"/>
            </conditions>
            <actions/>
        </model>
    </models>
    <components>
        <pagetitle model="AccountSummary" uniqueid="sk-1spZ2J-87">
            <maintitle>
                <template>{{Model.labelPlural}}</template>
            </maintitle>
            <subtitle>
                <template>Home</template>
            </subtitle>
            <actions>
                <action type="savecancel"/>
            </actions>
        </pagetitle>
        <skootable showconditions="true" showsavecancel="false" searchmethod="server" searchbox="true" showexportbuttons="false" pagesize="10" createrecords="true" model="AccountSummary" mode="read" allowcolumnreordering="true" uniqueid="sk-1spZ2J-88">
            <fields>
                <field id="Name" hideable="true" allowordering="true" uniqueid="fi-1spX6W-401"/>
                <field id="CreatedDate" hideable="true" allowordering="true" uniqueid="fi-1spX6W-402"/>
            </fields>
            <rowactions>
                <action type="multi" label="Show Details" icon="fa-search">
                    <actions>
                        <action type="setCondition" model="AccountDetails" condition="Id" value="{{Id}}"/>
                        <action type="requeryModels" behavior="standard">
                            <models>
                                <model>AccountDetails</model>
                            </models>
                            <onerroractions>
                                <action type="blockUI" message="There was an error" timeout="3000"/>
                            </onerroractions>
                        </action>
                    </actions>
                </action>
            </rowactions>
            <massactions usefirstitemasdefault="true"/>
            <views>
                <view type="standard"/>
            </views>
            <actions defaultlabel="Global Actions" defaulticon="sk-icon-magic" usefirstitemasdefault="true"/>
        </skootable>
        <grid uniqueid="sk-1sqKp3-192">
            <divisions>
                <division behavior="flex" verticalalign="top" minwidth="100px" ratio="1">
                    <components>
                        <template multiple="false" uniqueid="sk-1swAHu-404" model="AccountDetails" allowhtml="true">
                            <contents>&amp;lt;iframe frameborder="0" width="100%" height="800px" src="/apex/skuid__Social?id={{{Id}}}"/&amp;gt;</contents>
                        </template>
                    </components>
                </division>
                <division behavior="flex" verticalalign="top" minwidth="100px" ratio="1">
                    <components>
                        <basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="AccountDetails" buttonposition="" uniqueid="sk-1sqN6r-202" mode="read">
                            <columns>
                                <column width="100%">
                                    <sections>
                                        <section title="Section A">
                                            <fields>
                                                <field id="Name" valuehalign="" type=""/>
                                            </fields>
                                        </section>
                                    </sections>
                                </column>
                            </columns>
                        </basicfieldeditor>
                    </components>
                </division>
            </divisions>
            <styles>
                <styleitem type="background" bgtype="none"/>
            </styles>
            <renderconditions logictype="and">
                <rendercondition type="fieldvalue" enclosevalueinquotes="true" fieldmodel="AccountDetails" sourcetype="modelproperty" nosourcerowbehavior="deactivate" sourceproperty="hasRows"/>
            </renderconditions>
        </grid>
    </components>
    <resources>
        <labels/>
        <css/>
        <javascript/>
    </resources>
    <styles>
        <styleitem type="background" bgtype="none"/>
    </styles>
</skuidpage>

Thanks Bill. Yeah, I guess I will try that next. I want to have all the features of Chatter available, like who is following, additional posting type buttons, etc., but maybe I can build something like that that will suffice. It’s just a shame that I’d have to custom recreate nearly everything that is already there just to make it refresh on demand.

Thanks for that research and comprehensive reply, Barry. I’ll look into this option and give it a try.

that was very informative, thanks Barry

Glad it was helpful Andreas :slight_smile:

Hey Peter - Hopefully it will work out or Bill’s idea above will provide a path for you. Another option would be to write a custom component to handle everything but, as you mentioned above, its a lot of work to do for something that already (sort of) exists.

Skuid Team - Can you shed some light on whether or not current behavior of the Chatter component is intended or bugs?

One other note regarding the existing Chatter component, at least from what I remember, is that it can only exist on a page once.  Placing the chatter component on a page multiple times will cause issues.  If you need chatter on a page more than once (I did in my use case), you can use the Chatter component in one place and then the iframe solution in another (or iframe for everything which is the more straightforward approach).

Hi Peter, just to be sure: did you try using page includes with methods like what’s in this tutorial?

Great idea Mark! Page includes won’t work out of the box but with a little javascript, you can make it work.

The reason it won’t work out of the box is that Page Includes won’t respond to model data changes (they don’t have a “Model” property). Since the QueryString would need to be {{$Model.AccountDetails.data.0.Id}}, a change in the ID wouldn’t be detected (compared to having a model property of “AccountDetails” and value of {{Id}}). That said, you can use javascript to force a (re)load of the page include component when the row is clicked in the table (in the existing action sequence).

As the last step of your action sequence, the javascript would be:

skuid.$C("ChatterComponent").load();

It would be ideal for components like Chatter, Page Includes, etc. to respond to model changes (or at least document that they won’t) like most of the other components do but in the meantime, Mark’s idea of a Page include should theoretically work with the javascript caveat.

Here’s a sample based on the sample I provided below using the iframe. The left side of the grid is the iframe style, the right side of the grid is the page include style.

Main Page

<skuidpage unsavedchangeswarning="yes" personalizationmode="server" useviewportmeta="true" showsidebar="true" showheader="true" tabtooverride="Account">    <models>
        <model id="AccountSummary" limit="100" query="true" createrowifnonefound="false" datasource="salesforce" sobject="Account" type="">
            <fields>
                <field id="Name"/>
                <field id="CreatedDate"/>
            </fields>
            <conditions/>
            <actions/>
        </model>
        <model id="AccountDetails" limit="20" query="false" createrowifnonefound="false" datasource="salesforce" type="" sobject="Account">
            <fields>
                <field id="Name"/>
                <field id="Id"/>
                <field id="Description"/>
            </fields>
            <conditions>
                <condition type="fieldvalue" value="" enclosevalueinquotes="true" field="Id" state="filterableoff" inactive="true" name="Id"/>
            </conditions>
            <actions/>
        </model>
    </models>
    <components>
        <pagetitle model="AccountSummary" uniqueid="sk-1spZ2J-87">
            <maintitle>
                <template>{{Model.labelPlural}}</template>
            </maintitle>
            <subtitle>
                <template>Home</template>
            </subtitle>
            <actions>
                <action type="savecancel"/>
            </actions>
        </pagetitle>
        <skootable showconditions="true" showsavecancel="false" searchmethod="server" searchbox="true" showexportbuttons="false" pagesize="10" createrecords="true" model="AccountSummary" mode="read" allowcolumnreordering="true" uniqueid="sk-1spZ2J-88">
            <fields>
                <field id="Name" hideable="true" allowordering="true" uniqueid="fi-1spX6W-401"/>
                <field id="CreatedDate" hideable="true" allowordering="true" uniqueid="fi-1spX6W-402"/>
            </fields>
            <rowactions>
                <action type="multi" label="Show Details" icon="fa-search">
                    <actions>
                        <action type="setCondition" model="AccountDetails" condition="Id" value="{{Id}}"/>
                        <action type="requeryModels" behavior="standard">
                            <models>
                                <model>AccountDetails</model>
                            </models>
                            <onerroractions>
                                <action type="blockUI" message="There was an error" timeout="3000"/>
                            </onerroractions>
                        </action>
                        <action type="custom" snippet="reloadChatterPageInclude"/>
                    </actions>
                </action>
            </rowactions>
            <massactions usefirstitemasdefault="true"/>
            <views>
                <view type="standard"/>
            </views>
            <actions defaultlabel="Global Actions" defaulticon="sk-icon-magic" usefirstitemasdefault="true"/>
        </skootable>
        <grid uniqueid="sk-1sqKp3-192">
            <divisions>
                <division behavior="flex" verticalalign="top" minwidth="100px" ratio="1">
                    <components>
                        <template multiple="false" uniqueid="sk-1swAHu-404" model="AccountDetails" allowhtml="true">
                            <contents>&amp;lt;iframe frameborder="0" width="100%" height="800px" src="/apex/skuid__Social?id={{{Id}}}"/&amp;gt;</contents>
                        </template>
                    </components>
                </division>
                <division behavior="flex" verticalalign="top" minwidth="100px" ratio="1">
                    <components>
                        <basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="AccountDetails" buttonposition="" uniqueid="sk-1sqN6r-202" mode="read">
                            <columns>
                                <column width="100%">
                                    <sections>
                                        <section title="Section A">
                                            <fields>
                                                <field id="Name" valuehalign="" type=""/>
                                            </fields>
                                        </section>
                                    </sections>
                                </column>
                            </columns>
                        </basicfieldeditor>
                    </components>
                </division>
                <division behavior="flex" verticalalign="top" minwidth="100px" ratio="1">
                    <components>
                        <includepanel type="skuid" uniqueid="ChatterPageInclude" pagename="ChatterInclude" module="" querystring="id={{{$Model.AccountDetails.data.0.Id}}}" lazyload="false">
                            <renderconditions logictype="and"/>
                        </includepanel>
                    </components>
                </division>
            </divisions>
            <styles>
                <styleitem type="background" bgtype="none"/>
            </styles>
            <renderconditions logictype="and">
                <rendercondition type="fieldvalue" enclosevalueinquotes="true" fieldmodel="AccountDetails" sourcetype="modelproperty" nosourcerowbehavior="deactivate" sourceproperty="hasRows"/>
            </renderconditions>
        </grid>
    </components>
    <resources>
        <labels/>
        <css/>
        <javascript>
            <jsitem location="inlinesnippet" name="reloadChatterPageInclude" cachelocation="false">var params = arguments[0],
 $ = skuid.$;
skuid.$C("ChatterPageInclude").load();</jsitem>
        </javascript>
    </resources>
    <styles>
        <styleitem type="background" bgtype="none"/>
    </styles>
</skuidpage>

IncludePage

<skuidpage unsavedchangeswarning="yes" personalizationmode="server" useviewportmeta="true" showsidebar="true" showheader="true" tabtooverride="Account">    <models>
        <model id="Account" limit="1" query="true" createrowifnonefound="false" datasource="salesforce" sobject="Account" type="">
            <fields>
                <field id="Name"/>
                <field id="CreatedDate"/>
            </fields>
            <conditions>
                <condition type="param" enclosevalueinquotes="true" operator="=" field="Id" value="id"/>
            </conditions>
            <actions/>
        </model>
    </models>
    <components>
        <social uniqueid="sk-27F_kB-117" feedtype="" model="Account" showtopics="true"/>
    </components>
    <resources>
        <labels/>
        <css/>
        <javascript/>
    </resources>
    <styles>
        <styleitem type="background" bgtype="none"/>
    </styles>
</skuidpage>

Yep, this is exactly right, Barry. To explain why that’s the case, I’ll borrow from Zach on this post: “Skuid basically uses the Salesforce chatter:feedWithFollowers component, internally, and this component, as we have verified through communications directly with Salesforce engineers, was ‘not built to be used more than once on a single page’”.

Barry,
For now, it is intended that the Chatter component doesn’t refresh when the model refreshes. However, I agree that that’s not really ideal, especially when compared with the functionality of our other components. I’ll bring it up to the team as an enhancement and see if there’s a reason why we wouldn’t want to have it function that way. 

Thanks!

Thanks for the confirmation Amy.  Can this be reflected in the docs somewhere?

Thanks Amy.  Hoping that the enhancements will be forthcoming :slight_smile:  In the meantime, can this be reflected in the docs somewhere?

Thanks all. For completeness, I’ve created a Skuid “Idea” for an improved Chatter component. Hopefully an progress toward doing so can show up there.

This is good, thanks so much for the sample code, Barry. I had tried an include, but couldn’t get the passing of IDs to work right for some reason. This pretty much resolves the immediate need, aside from the funky formatting attributed to using a template. This is also a bit faster than using a straight include (based in previous reply example from Barry), I assume due to the fact that it doesn’t have to load a complete new page and model.

Glad it worked out for you Peter.  It would take some testing to truly determine which approach is faster.  On average, I was seeing the template/iframe approach typically resolving faster than page include approach but not every time.  You’re right, saving the model load helps for sure.  Either approach is making a network call but I believe there will be more network calls in the page include approach than the iframe approach since it doesn’t incur the full “skuid page” overhead.  If it were me and performance aside, I’d stick with the iframe simply because it’s less maintenance than the page include approach long term (it’s also a more portable solution for other SObject feeds).

Totally agreed - that’s what I’m trying. This is a bit of a side project, but if I find some good further pointers with this I’ll try to post back here.