Customizing ActivityFeed markup
ActivityFeed control uses KnockoutJS framework for client side presentation logic and view rendering.
The control has been designed to allow easy modification of view templates. Below is a snippet ilustrating how various parts of ActivityFeed control markup can be customized. All the templates can use KnockouJS
data binding syntax.
<ow:ActivityFeed runat="server">
<Template>
<textarea data-bind="value: newPostText" />
<button data-bind="command: postUpdate">Post</button>
<ow:UpdatesPlaceholder runat="server" />
<button data-bind="command: loadNextPage">Next page</button>
<Template>
<UpdateTemplate>
<ow:UpdateContentPlaceholder runat="server" />
<ow:CommentsPlaceholder runat="server" />
</UpdateTemplate>
<CommentTemplate>
</CommentTemplate>
<UpdateContentTemplates>
<ow:UpdateContentTemplate runat="server" ContentType="post">
</ow:UpdateContentTemplate>
</UpdateContentTemplates>
</ow:ActivityFeed>
Template
Template is rendered as a root element of ActivityFeed control. It is bound to ActivityFeedViewModel object.
Default template contains markup to publish new update, refresh updates, search updates and a container for updates list.
<%--Post status updates--%>
<fieldset class="update"
data-bind="jQuery: 'fakeInput', css: { disabled: !postUpdate.isInProgress() }">
<legend class="hidden-visualy">What are you doing?</legend>
<textarea cols="61" rows="2"
data-bind="valueExt: newPostText, enable: !postUpdate.isInProgress(), valueUpdate: 'afterkeydown'">
</textarea>
<button class="right" data-bind="command: postUpdate">Update</button>
</fieldset>
<%--Refresh updates--%>
<div class="refresh-updates">
<button data-bind="click: refreshUpdates">Refresh updates</button>
</div>
<%--Search updates--%>
<fieldset class="update search">
<label for="txtSearch">Search:</label>
<input id="txtSearch" type="text" class="watermark"
data-bind="value: searchTerm, valueUpdate: 'afterkeydown'"
title="Write a message...">
</input>
</fieldset>
<%--Updates placeholder--%>
<af:UpdatesPlaceHolder cssclass="post-list" runat="server"/>
ActivityFeedViewModel is a javascript based object responsible for passing and retrieving data to view.
ActivityFeedViewModel properties:
- updates - observable array of loaded updates
- newPostText - text to be posted as a user entered post
- searchTerm - text to be used for full text filtering of the feed
- hasMoreUpdates - bool indicating if there is more updates to be loaded (due to page size limit)
ActivityFeedViewModel commands:
- postUpdate - publish status update
- loadNextPage - load next package of updates based on pageSize
- refreshUpdates - refresh list of updates; first page is loaded
- hasMoreUpdates - used to check if there is more updates on server
- searchUpdates - filter updates based on searchTerm
UpdatesPlaceholdercontrol - indicates where the list of updates will be rendered. It renders them inside <ul> element.
UpdateTemplate
is used to render each of the updates in UpdatesPlaceholder.
UpdateContentPlaceholder - this is where the update content will be rendered. The template will be selected from UpdateContentTemplates by matching content type.
CommentsPlaceholder
Renders a list of comments for a given update.
<af:CommentsPlaceHolder runat="server" />
CommentTemplate
Renders comment item.
<li>
<article>
<footer>
<figure>
<img alt="Placeholder image from flickholdr.com"
data-bind="attr: { 'src': publisher().profile.photoUrl,
'alt': publisher().profile.name }"
width="30px" height="30px" />
</figure>
</footer>
<div class="content-wrapper">
<p><a href="javascript:void(0);" title="palceholder"
data-bind='text: publisher().profile.name'></a></p>
<p data-bind="text: text"></p>
</div>
<div class="utils">
<ul>
<!-- ko if: removeComment.isAllowed -->
<li><a href="javascript:void(0);" title="placeholder"
data-bind="command: removeComment, commandConfirm: function ()
{ return confirm('Remove comment?'); }">Delete</a></li>
<!-- /ko -->
<li data-bind="attr: { 'title': publishedOn() }">
<p><time datetime="2012-05-23T12:12+02:00"
data-bind="text: moment(publishedOn()).fromNow()"></time></p></li>
</ul>
</div>
</article>
</li>
Update contents templates
Each update content type requires UpdateContentTemplate defined in ActivityFeed control. There is default implementation for 'post' content type.
<af:UpdateContentTemplate runat="server" ID="postTemplate" ContentType="post">
<div class="content-wrapper postContent">
<p>
<a href="javascript:void(0);" title="palceholder"
data-bind="text: publisher().profile.name"></a>
</p>
<p data-bind="text: content().text"></p>
</div>
</af:UpdateContentTemplate>
To define new update content type template, add new UpdateContentTemplate control as a child of UpdateContentTemplates. UpdateContentTemplate requires ContentType property.
<af:ActivityFeed runat="server" FeedType="Global">
<UpdateContentTemplates>
<af:UpdateContentTemplate runat="server" ContentType="contentTypeA">
...
</af:UpdateContentTemplate>
<af:UpdateContentTemplate runat="server" ContentType="contentTypeShareB">
...
</af:UpdateContentTemplate>
<af:UpdateContentTemplate runat="server" ContentType="contentTypeShareC">
...
</af:UpdateContentTemplate>
</UpdateContentTemplates>
</af:ActivityFeed>
During rendering ActivityFeed will select update content template based on contentType property of update. When ActivityFeedViewModel will not find content template, then
'Template /Content Type/ not found' text will appear.