<!DOCTYPE html>
<html ng-app="viewheadExample">
<head>
<title ng-bind="viewTitle ? viewTitle + ' - viewhead example' : 'viewhead example'">viewhead example</title>
<script src="http://code.angularjs.org/1.2.4/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.js"></script>
<script src="angularjs-viewhead.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
</head>
<body>
<header class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<a ui-sref="home" class="navbar-brand">viewhead example - FIXED</a>
</div>
<nav>
<ul class="nav navbar-nav">
<li><a ui-sref="subviews">Sub-Views</a>
</li>
<li><a ui-sref="subviews.one">Sub-View One</a>
</li>
<li><a ui-sref="dynamic-title">Dynamic Title</a>
</li>
<li>
<a ui-sref="rss-link">RSS Linking</a></li>
</ul>
</nav>
</div>
</header>
<div class="container " ui-view ng-cloak>
</div>
<script id="partials/subviews.html " type="text/ng-template ">
<h1 view-title>Subviews</h1>
<p>This view contains a named ui-view.</p>
<p>
<a ui-sref=".one ">One</a> |
<a ui-sref=".two ">Two</a>
</p>
<div ui-view="content "></div>
</script>
<script id="partials/subviews/one.html " type="text/ng-template ">
<h2 view-title>Subview One</h2>
<p>This is the first sub-view.</p>
</script>
<script id="partials/subviews/two.html " type="text/ng-template ">
<h2 view-title>Subview Two</h2>
<p>This is the second sub-view.</p>
</script>
<script id="partials/dynamic-title.html " type="text/ng-template ">
<h1>Dynamic Title</h1>
<p>The view title can include interpolated template expressions, just like any other element.
The text field below is bound to the variable <code>chosenTitle</code> in the scope, and then
the <code>view-title</code> directive also binds to that same variable, allowing the title
to be set dynamically. Try changing the text below and watch as the document title changes.</p>
<form class="form-inline ">
<div class="form-group ">
<input ng-model="chosenTitle " size="40 ">
</div>
</form>
<view-title>{{chosenTitle}}</view-title>
</script>
<script id="partials/home.html " type="text/ng-template ">
<h1 view-title>About</h1>
<p>This is a simple demonstration of the viewhead module for AngularJS. This module allows the view
template to contribute a per-view part to the page title, and to configure other elements that should appear
in the head of the document whenever the view is displayed.</p>
<p>Navigate to the other pages in this demonstration to see the page title change. You can also use your
browser's DOM inspector to watch the other head elements come and go.</p>
<p>This page demonstrates how the view title can be automatically derived from an existing element in
the document by simply adding the <code>view-title</code> attribute. The 'About' header above has this
attribute, and so its content is automatically propagated to the page title.</p>
</script>
<script id="partials/rss-link.html " type="text/ng-template ">
<h1 view-title>RSS Linking</h1>
<link view-head rel="alternate " type="application/rss+xml " href="{{ rssUrl }} ">
<p>Some browsers support subscribing to an RSS feed linked from the document's head. This is one example
of an element you may wish to introduce into the head of the document on a per-view basis.</p>
<p>If your browser supports RSS subscription you may see an RSS icon available for this page. If not, use
your browser's DOM inspector to look at the head of the document and note the <code>link</code> element.
This will go away when you navigate to one of the other pages in this demo, and come back when you return.</p>
<p>Most consumers of RSS metadata are robots that can't run the JavaScript on an HTML page, so for best
results this technique can be combined with a mechanism that creates static snapshots of pages to serve
to robots. That's obviously out of scope for this module, but at the time of writing several services
are available that provide such a snapshot feature.</p>
</script>
</body>
</html>
var app = angular.module('viewheadExample', ['ng', 'ui.router', 'viewhead']);
app.config(
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("home", {
url: "/",
templateUrl: 'partials/home.html'
})
.state("subviews", {
url: "/subviews",
templateUrl: 'partials/subviews.html',
controller: function($scope) {
},
})
.state("subviews.one", {
url: "/one",
views: {
content: {
templateUrl: 'partials/subviews/one.html'
}
}
})
.state("subviews.two", {
url: "/two",
views: {
content: {
templateUrl: 'partials/subviews/two.html'
}
}
})
.state("dynamic-title", {
url: "/dynamic-title",
templateUrl: 'partials/dynamic-title.html',
controller: function($scope) {
$scope.chosenTitle = 'Dynamic Title';
}
})
.state("rss-link", {
url: "/rss-link",
templateUrl: 'partials/rss-link.html',
controller: function($scope) {
$scope.rssUrl = 'example.rss';
}
});
}
);
/* Styles go here */
When using `ui-sref` for navigating to another `ui-view`, the `$rootScope.viewTitle`
is deleted before it can be set by the `view-title` in the destination view.
This causes a flicker in the window title and, worse, the incorrect title shows
up in the browser history.
This version fixes the issue.
(function (angular, document) {
var mod = angular.module('viewhead', []);
var title;
mod.directive(
'viewTitle',
['$rootScope', function ($rootScope, $timeout) {
return {
restrict: 'EA',
link: function (scope, iElement, iAttrs, controller, transcludeFn) {
// If we've been inserted as an element then we detach from the DOM because the caller
// doesn't want us to have any visual impact in the document.
// Otherwise, we're piggy-backing on an existing element so we'll just leave it alone.
var tagName = iElement[0].tagName.toLowerCase();
if (tagName === 'view-title' || tagName === 'viewtitle') {
iElement.remove();
}
scope.$watch(
function () {
return iElement.text();
},
function (newTitle) {
$rootScope.viewTitle = title = newTitle;
}
);
scope.$on(
'$destroy',
function () {
title = undefined;
$timeout(function() {
if(!title) {
delete $rootScope.viewTitle;
}
});
}
);
}
};
}]
);
mod.directive(
'viewHead',
function () {
var head = angular.element(document.head);
return {
restrict: 'A',
link: function (scope, iElement, iAttrs, controller, transcludeFn) {
// Move the element into the head of the document.
// Although the physical location of the document changes, the element remains
// bound to the scope in which it was declared, so it can refer to variables from
// the view scope if necessary.
head.append(iElement);
// When the scope is destroyed, remove the element.
// This is on the assumption that we're being used in some sort of view scope.
// It doesn't make sense to use this directive outside of the view, and nor does it
// make sense to use it inside other scope-creating directives like ng-repeat.
scope.$on(
'$destroy',
function () {
iElement.remove();
}
);
}
};
}
);
})(angular, document);