SOLUTION FOUND!!! The suggestion by Mohith Kumar to use actionFunction was the correct way to go. It just took a little research and better understanding of how actionFunction works to implement the solution. After some googling, I finally found a blog post (http://shivasoft.in/blog/salesforce/passing-parameter-in-actionfunction-in-visualforce/) that really helped me understand how to use actionFunction for my solution.
Here is the final code for the controller extension:
public with sharing class CanNewsChatterFeedExtension {
//declare class variables
public String feedItemId {get;set;}
public String likeId {get;set;}
public String commentToAdd {get;set;}
// Constructor. Needed to use as an extension.
public CanNewsChatterFeedExtension(CanNewsChatterFeedController custController) {}
//call via javascript and actionFunction. Class variables feedItemId and commentToAdd are set by passing params via actionFunction
public void addCommentToFeedItem(){
try{
FeedComment fcomment = new FeedComment();
fcomment.FeedItemId = feedItemId;
fcomment.CommentBody = commentToAdd; //String.escapeSingleQuotes(commentToAdd); <--prevents SOQL injection. Necessary?
insert fcomment;
}catch(Exception e){
System.debug(e.getMessage());
}
}
//call via javascript and actionFunction. Class variable feedItemId is set by passing params via actionFunction
public void addLikeToFeedItem(){
try{
ConnectApi.ChatterFeeds.likeFeedItem(null, this.feedItemId);
}catch(Exception e){
System.debug(e.getMessage());
}
}
//call via javascript and actionFunction. Class variable likeId is set by passing params via actionFunction
public void removeLikeFromFeedItem(){
try{
ConnectApi.ChatterFeeds.deleteLike(null, this.likeId);
}catch(Exception e){
System.debug(e.getMessage());
}
}
}
And the VF page final code:
<apex:page controller="CanNewsChatterFeedController" extensions="CanNewsChatterFeedExtension" id="feedcontroller" sidebar="false" showHeader="false" standardStylesheets="false" >
<script type="text/javascript">
var j$ = jQuery.noConflict();
/*called via JS onclick to pass in the itemId.
When addLike() is called it fires the actionFunction of the same name defined below,
which in turn passes the param to the apex class, sets the class variable and calls the defined apex method.
*/
function likeFeedItem(itemId){
addLike(itemId);
}
/*called via JS onclick to pass in the likeId.
When removeLike() is called it fires the actionFunction of the same name defined below,
which in turn passes the param to the apex class, sets the class variable and calls the defined apex method.
*/
function unLikeFeedItem(likeId){
removeLike(likeId);
}
//displays the commentBox for adding new comments
function showCommentBox(itemId){
j$('#comBox_'+itemId).show();
}
//clears the text in the commentBox onFocus
function clearCommentToAdd(itemId){
j$('#comIn_'+itemId).val('');
}
/*called via JS onclick to pass in the itemId.
When addComment() is called it fires the actionFunction of the same name defined below,
which in turn passes the params to the apex class, sets the class variables and calls the defined apex method.
*/
function addCommentToFeedItem(itemId){
var commentToAdd = j$('#comIn_'+itemId).val();
addComment(itemId, commentToAdd);
}
</script>
<style type="text/css">
.....
</style>
<div id="feed-display-div">
<apex:form >
<apex:pageMessages />
<apex:actionPoller action="{!refreshFeed}" rerender="feedRows" interval="60" />
<apex:outputPanel id="feedRows" >
<apex:repeat value="{!newsFeedForDisplay}" var="feedItemInfo" rows="10" rendered="{!NOT(ISNULL(newsFeedForDisplay))}">
<div class="row">
<div>
<div style="display:inline-block;vertical-align:top;">
<a href="/{!feedItemInfo.userId}">
<apex:image style="margin:4px" width="40" url="{!feedItemInfo.feedItem.photoUrl}"/>
</a>
</div>
<div style="display:inline-block;vertical-align:top;width:220px; color:#087bf5;">
<a href="/{!feedItemInfo.userId}">
<b>{!feedItemInfo.userName}</b>
</a><br/>
<apex:outputText style="color:#000000;" value="{!feedItemInfo.formattedText}" />
<apex:outputPanel layout="block" rendered="{!IF(feedItemInfo.linkUrl == null, false, true)}" >
<a href="{!feedItemInfo.linkUrl}">
<b>{!feedItemInfo.linkTitle}</b>
</a>
</apex:outputPanel>
<div class="comLikeDiv">
<a href="#" onclick="showCommentBox('{!feedItemInfo.feedItemId}')">Comment</a><span class="separatorDot">.</span>
<apex:outputPanel rendered="{!IF(feedItemInfo.isLikedByUser == true, false, true)}">
<span class="likeSpan" onclick="likeFeedItem('{!feedItemInfo.feedItemId}')">Like</span>
</apex:outputPanel>
<apex:outputPanel rendered="{!IF(feedItemInfo.isLikedByUser == false, false, true)}">
<span class="likeSpan" onclick="unLikeFeedItem('{!feedItemInfo.myLikeId}')">Unlike</span>
</apex:outputPanel>
<span class="separatorDot">.</span>
<apex:outputText styleClass="createdDate" value="{!feedItemInfo.relativeCreatedDate}" />
</div>
</div>
<apex:outputPanel layout="block" rendered="{!IF(feedItemInfo.contentDownloadUrl != null && feedItemInfo.imageUrl != null && feedItemInfo.hasImagePreview, true, false)}" >
<div style="left: 3em; width: 85%; padding: 2px; position: relative;">
<apex:image style="margin:4px" width="60" url="{!feedItemInfo.imageUrl}"/>
<div style="float:right;position:relative;padding:2px;width:65%;">
<a href="{!feedItemInfo.contentDownLoadUrl}">Download:<br />{!feedItemInfo.contentTitle }</a>
</div>
</div>
</apex:outputPanel>
</div>
<apex:outputPanel rendered="{!IF(feedItemInfo.likesMessage != '', true, false)}">
<div class="likeBox">
<div style="margin-left:4px;width:95%;">
<div style="display:inline-block;vertical-align:top;">
<apex:image value="{!$Resource.LikeThumb}" />
</div>
<div style="display:inline-block;width:80%;padding-top:2px;">
<apex:outputText style="margin-left:4px;" value="{!feedItemInfo.likesMessage}" />
</div>
</div>
</div>
</apex:outputPanel>
<apex:outputPanel rendered="{!IF(feedItemInfo.commentCount > 0, true, false)}">
<div class="commentBox">
<apex:repeat value="{!feedItemInfo.comments}" var="commentInfo">
<div class="commentBox-inner">
<div style="display:inline-block;vertical-align:top;">
<apex:image style="margin:4px" width="25" url="{!commentInfo.comment.user.photo.smallPhotoUrl}"/>
</div>
<div style="display:inline-block;vertical-align:top;width:80%">
<a href="/{!commentInfo.userId}">
<b>{!commentInfo.userName}</b>
</a>:
<apex:outputText value="{!commentInfo.formattedText}" />
<div class="createdDate">
<apex:outputText value="{!commentInfo.relativeCreatedDate}" />
</div>
</div>
<apex:outputPanel rendered="{!IF(commentInfo.imageUrl == null, false, true)}" >
<div style="padding-left:4px;display:inline-block;vertical-align:top;float:left;position:relative;width:95%" >
<apex:image style="padding:4px" width="60" url="{!commentInfo.imageUrl}"/>
<div style="float:right;position:relative;padding:2px;width:65%;">
<a href="{!commentInfo.contentDownLoadUrl}">Download:<br />{!commentInfo.contentTitle }</a>
</div>
</div>
<div style="clear: both;" />
</apex:outputPanel>
</div>
</apex:repeat>
</div>
</apex:outputPanel>
<div class="addComment" id="comBox_{!feedItemInfo.feedItemId}">
<input type="text" id="comIn_{!feedItemInfo.feedItemId}" style="border:1px solid #AAAAAA;" value="Add a comment..." onfocus="clearCommentToAdd('{!feedItemInfo.feedItemId}')" />
<apex:commandButton onclick="addCommentToFeedItem('{!feedItemInfo.feedItemId}')" value="Comment" rerender="feedRows" />
</div>
</div>
</apex:repeat>
<apex:actionFunction name="addLike" action="{!addLikeToFeedItem}" rerender="feedRows">
<apex:param name="theItemId" assignTo="{!feedItemId}" value="" />
</apex:actionFunction>
<apex:actionFunction name="removeLike" action="{!removeLikeFromFeedItem}" rerender="feedRows">
<apex:param name="theLikeId" assignTo="{!likeId}" value="" />
</apex:actionFunction>
<apex:actionFunction name="addComment" action="{!addCommentToFeedItem}" rerender="feedRows">
<apex:param name="theItemId" assignTo="{!feedItemId}" value="" />
<apex:param name="theComment" assignTo="{!commentToAdd}" value="" />
</apex:actionFunction>
</apex:outputPanel>
</apex:form>
</div>
I was having a similar issue with IE11. I had a Visualforce form, and sections of the page were re-rendered. In IE11, some empty tags were problematic. This included tags used for clearing and <apex:inputTextarea>.
To correct the div, I inserted the html code for a space( ). For example: <div class="clearer"> </div>
To correct the apex:inputTextarea, I had to make sure that it was never rendered empty. In the controller, I added at least three blank spaces in the field used by the textarea when the field didn't contain text. The tag would then rerender showing one blank space. Adding only one or two spaces didn't work, not sure why. Before processing, I trimmed any left blanks.
Example apex:
if (MyTextAreaField == null || MyTextAreaField.trim().length() == 0) MyTextAreaField = ' ';
Best Answer
Similar to Keith's answer, the important part is the value of the name attribute on the facet tag.
If the 'add' link is supposed to be on each row rather than rendered in the header cell: