1. Technical
  2. Organizing a Live Center Channel

Organize your Live Blog using tags

Using Live Center you can divide your live blog into different tabs based on their tags. This allows you to categorize posts and provide your readers with a way to quickly find posts they are interested in. 

As an example, you can create a continuous News Feed with the tabs News, Sports, and Weather. When a user clicks a tab, the live blog will filter the whole feed to show only posts relating to that topic. 

A live example of a Live Blog which uses these features can be found here in a Football Transfer Window Live Blog created by Nettavisen.no

This can be achieved using out of the box functionality in Live Center. Below is a short guide to help you set up such a feed.

Introduction

Live Center relies on tags to categorize posts into one or more tabs. Posts are easily tagged when they are created and therefore are easily added by your content creators when they publish posts.

In addition to categorizing posts into tabs, tags can also be used by users to see only posts with that specific tag.

For example, as you can see in the screenshots below we have added 3 tags: “ Tab1”, “Vålerenga” and “Real Madrid”. Tab1 is the tag which we use to categorize posts into a tab, while Vålerenga and Real Madrid are shown to readers and is available to click to see only posts with a specifc tag.

Because “Tab 1” is the name of the tab, we hide this tag from tags below the content. We will provide more details about how to do this in the following steps. 

Useful resources before you start

Introduciton to how you can add Live Center can be found here: Adding Live Center

Throughout this guide, we will use some methods from NcPost and NcCore namespaces that are introduced in more detail here: https://livecentersupport.norkon.net/category/21-technical

1. Create containers for tabs and highlighted tags in .html file

2. Use NcPosts.hookOptionsAfterDefaults to customize the feed included header, content, comments, and tags with channel “options” object

3. Getting the feed control object to faciliate changing tags filter after the fact

4. Set the default initial tab

5. Hook to the tabs panel to inject them

6. Use hookTabs() to allow us to create a tabs panel using NcHtml. 

ToggleActiveClass() toggle classes according to selected tab. With the function selectTag() we use to filter posts by selected tag/tab

7. Next we check if we have different initial tag selected

getSelecedTagFromUrl() enable us to check specific tag highlighted.

8. Check if there is a post to be highlighted

getLinkedPostIdFromUrl()

9. Replace the default tags renderer with your custom.

The default one doesn’t allow us to sort posts by tags. To use tabs also we need to create our own named ExampleTagsRenderer()

10. Our own Tags Renderer: 

11. Add the comments to the top

12. Replace the default comments renderer with our own implementation of ExampleCommentsRenderer();

13. Our own implementation 

14. Replace the question renderer to our own implementation

15. Our own question renderer:


Additional functionality

A very important question is: How to inject ads to this feed which is basically very common practice? 

We can easily customize that and inject the ad to the channel. The following example will show ad implementation with the ad on the 2nd position and from that- every 5 posts. 

We can see how the interval is set and how the feed behaves when the is post highlighted on the top. 

NcPosts.hookOptionsAfterDefaults(options => {

let pinnedPostExists = false;
options.initialDataReceived = NcPosts.appendFunction(options.initialDataReceived, d => {
if (d && d.channelContent && d.channelContent.pinnedPostId) {
pinnedPostExists = true;
}
});

let counter = 1000;

//Hook in ads on lists
options.listManagerCreated = NcPosts.appendFunction(options.listManagerCreated, (listManager: NcPosts.PostListManager) => {

type AdFactory = () => { html: string, postFunc?: () => void };

const adFactories = {
desktopAd: () => {
const newId = "test-slot-" + (++counter);
return {
html: "<div id='" + newId + "'></div>",
postFunc: () => {

const win = <any>window;
const googletag = win.googletag = win.googletag || {};
googletag.cmd = googletag.cmd || [];
console.log("Rendering desktop");

googletag.cmd.push(function () {
googletag.defineSlot('test', [[480, 400], [580, 400], [580, 500], 'fluid'], newId).addService(googletag.pubads());
googletag.display(newId);
});
}
};
},
mobileAd: () => {
const newId = "test-slot-mobile-" + (++counter);
return {
html: "<div id='" + newId + "'></div>",
postFunc: () => {

const win = <any>window;
const googletag = win.googletag = win.googletag || {};
googletag.cmd = googletag.cmd || [];
console.log("Rendering mobile");

googletag.cmd.push(function () {
googletag.defineSlot('test', [[300, 250], [320, 250], [320, 480], 'fluid'], newId).addService(googletag.pubads());
googletag.display(newId);
});

}
};
}
}

const buildAdElement = (adFactory: AdFactory) => {
const adHtml = adFactory();

return {
element: NcHtml.create("div.ad-wrapper", {
style: "width: 100%; border: 0; text-align: center; margin-bottom: 30px",
_children: [
{ _tag: "p.ncpost-content-ad", _text: "advertisement" },
{ _tag: "div", _html: adHtml.html }
]
}), postFunc: adHtml.postFunc
};
}

const injectAdAfter = (adFactory: AdFactory, container: HTMLElement) => {
const ad = buildAdElement(adFactory);
container.parentNode.insertBefore(ad.element, container.nextSibling);
ad.postFunc && ad.postFunc();
};

const injectAdBefore = (adFactory: AdFactory, container: HTMLElement) => {
const ad = buildAdElement(adFactory);
container.parentNode.insertBefore(ad.element, container);
ad.postFunc && ad.postFunc();
};

//Hook into to display rolling ads for every 10th post
let lastRollingAdBinding: NcPosts.PostListBinding = null;
let adFactory: AdFactory = null;

// watching for screen width resize
function widthChange(watcher) {
if (watcher.matches) {
adFactory = adFactories.mobileAd;
} else {
adFactory = adFactories.desktopAd;
}
}

if (matchMedia) {
const widthWatcher = window.matchMedia("(max-width: 580px)");
widthWatcher.addListener(widthChange);
widthChange(widthWatcher);
} else {
adFactory = adFactories.desktopAd;
}


//We observe changes to the list and add the appropriate ads at the appropriate locations
listManager.listChanged = NcPosts.appendFunction(listManager.listChanged, (postBindings: NcPosts.PostListBinding[]) => {

//If there's no ad set before
if (!lastRollingAdBinding && postBindings.length >= 1) {
lastRollingAdBinding = postBindings[0];

if (pinnedPostExists) {
injectAdBefore(adFactory, lastRollingAdBinding.container);
} else {
injectAdAfter(adFactory, lastRollingAdBinding.container);
}
}

const everyNPosts = 3;

let adIndex = postBindings.indexOf(lastRollingAdBinding);
if (adIndex >= 0) {

let newRollingAdBinding: NcPosts.PostListBinding;

while (newRollingAdBinding = postBindings[adIndex + everyNPosts]) {
adIndex += everyNPosts;
lastRollingAdBinding = newRollingAdBinding;


if (pinnedPostExists) {
injectAdBefore(adFactory, lastRollingAdBinding.container);
} else {
injectAdAfter(adFactory, lastRollingAdBinding.container);
}
}
}
});
});

});

 

Here you can see the result: