Dynamic Service Portal Breadcrumbs, Enhanced

“Build your own dreams, or someone else will hire you to build theirs.”
Farrah Gray

While I was playing around with my Configuration Item Icon Assignment widget, it occurred to me that it would be beneficial for my Dynamic Service Portal Breadcrumbs widget to somehow announce its presence to other widgets on the same page, and to provide them with the URL of previous page in case they wanted to set up some kind of Cancel or Done button that returned the user to the page from which they came. I even thought about putting such a button on the breadcrumbs widget itself, just under the row of breadcrumbs, but that seemed a little out of place, the more that I thought about it. So right now, I just want to let the other widgets know that they are sharing the page with the breadcrumbs widget and provide them with the return path.

The easiest way to do that is through some kind of root scope broadcast message, but that does present a little bit of a wrinkle, as you don’t really know which widgets have loaded first, and you could be broadcasting to no one if you are first to arrive on the scene. To solve that problem, you can listen for a different root scope message from other widgets that basically asks, “Please say that again now that I am ready to hear it.” From the listener’s perspective then, when you are ready to utilize the return path, if you haven’t yet received it, you can request it, and then go from there.

But first things first. Before we can announce the return path, we need to know what it is. Using the length of the breadcrumbs array as an index takes you past the end of the array, but subtracting 1 from that value gets you to the last entry, which happens to be the current page. We want the page before that, so we have to subtract 2 from the length. The danger there, of course, is that you might have an empty array or an array with a single element, and subtracting 2 would again put your index outside of the range of the array. So we have to start out with a default value, and then check the length of the array before we proceed any further. My code to do that looks like this:

c.returnPath = '?';
if (c.breadcrumbs.length > 1) {
	c.returnPath = c.breadcrumbs[c.breadcrumbs.length - 2].url;
}

That establishes the return path. Now we have to announce it. I created a function for that, since we will be doing this from multiple places (on load, and then again on request). Here is my function:

function shareReturnPath() {
	$rootScope.$broadcast('snhBreadcrumbs', {
		returnPath: c.returnPath
	});
}

Now that we have created the function, we need to call it as soon as we establish the return path, and then we need to set up a listener for later requests that will call it again on demand. For that, we just need a little more code wedged in between the two blocks above:

shareReturnPath();
$rootScope.$on('snhShareReturnPath', function() {
	shareReturnPath();
});

Well, that wasn’t so bad. Combined with what we had before, the entire Client Controller now looks like this:

function snhBreadcrumbs($scope, $rootScope, $location, spUtil) {
	var c = this;
	c.expanded = !spUtil.isMobile();
	c.expand = function() {
		c.expanded = true;
	};
	c.breadcrumbs = [];
	var thisTitle = c.data.page || document.title;
	var portalSuffix = ' - ' + $rootScope.portal.title.trim();
	var cutoff = thisTitle.indexOf(portalSuffix);
	if (cutoff != -1) {
		thisTitle = thisTitle.substring(0, cutoff);
	}
	var thisPage = {url: $location.url(), id: $location.search()['id'], label: thisTitle};
	
	if (thisPage.id && thisPage.id != $rootScope.portal.homepage_dv) {
		var pageFound = false;
		for (var i=0;i<c.data.breadcrumbs.length && !pageFound; i++) {
			if (c.data.breadcrumbs[i].id == thisPage.id) {
				c.breadcrumbs.push(thisPage);
				pageFound = true;
			} else {
				c.breadcrumbs.push(c.data.breadcrumbs[i]);
			}
		}
		if (!pageFound) {
			c.breadcrumbs.push(thisPage);
		}
	}
	c.data.breadcrumbs = c.breadcrumbs;
	c.server.update();
	c.returnPath = '?';
	if (c.breadcrumbs.length > 1) {
		c.returnPath = c.breadcrumbs[c.breadcrumbs.length - 2].url;
	}
	shareReturnPath();
	$rootScope.$on('snhShareReturnPath', function() {
		shareReturnPath();
	});

	function shareReturnPath() {
		$rootScope.$broadcast('snhBreadcrumbs', {
			returnPath: c.returnPath
		});
	}
}

Yes, we still have to test it, just to make sure that it all works in the way in which we intended, but it should work and it didn’t take all that much to provide this new functionality. In fact, it will probably be more work to test it than it was to create it. I’ll have to find or build another widget to share the page with this one, then create a page to put them both on, and then bring up the page and try things out. Now that I think about it, that seems like a decent amount of effort, so I think I will just save all of that until next time!