var myapp = angular.module('myapp', ["ui.router", "pasvaz.bindonce"]);
myapp.filter('startFrom', function() {
  return function(input, start) {
    start = +start; //parse to int
    return input.slice(start);
  }
});

myapp.config(function($stateProvider, $urlRouterProvider) {

  $urlRouterProvider.when("/slowcart", function ($timeout, $state) {
                    $timeout(function () {
                        $state.transitionTo("index2", null, {
                            location: "replace",
                            inherit: false,
                            notify: true
                        });
                    }, 4000);

                    return true; //Yes, we handled it...though not for a while after this.
                });
  $stateProvider
    .state('index', {
      abstract: true,
      url: "/",
      views: {
        "table": {
          templateUrl: "tableTemplate.html",
          controller: function($scope, $injector) {
            $scope.date = Date.now;
            $scope.rows = [];
            for (var i = 0; i < 100; i++) {
              $scope.rows.push({
                number: i,
                description: "boo" + i
              });
            }

            $scope.getPreviousPage = function() {
              var $stateParams = $injector.get('$stateParams');
              return parseInt($stateParams.number) - 1;
            }

            $scope.getNextPage = function() {
              var $stateParams = $injector.get('$stateParams');
              return parseInt($stateParams.number) + 1;
            }
          }
        }
      }
    });
  $stateProvider.state('index.withOptions', {
    url: "?number&size",
    onEnter: function($rootScope, $stateParams) {
      // this is a hack to prove the point...imagine
      // better code to manage these properties here.
      $rootScope.pageNumber = $stateParams.number;
      $rootScope.size = $stateParams.size;
    },
    onExit: function($rootScope) {

      delete $rootScope.pageNumber;
      delete $rootScope.size;
    }
  });
  $stateProvider.state('index2', {
    url: "/index2?number",
    views: {
      "table": {
        templateUrl: "otherTemplate.html"
      }
    },
    onEnter: function($rootScope, $stateParams) {
      // this is a hack to prove the point...imagine
      // better code to manage these properties here.
      $rootScope.index2Number = $stateParams.number;
    },

    onExit: function($rootScope) {
      delete $rootScope.index2Number;
    }
  });
});
<!DOCTYPE html>
<html ng-app="myapp">

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Angular UI-router child state with no template</title>
    <!-- Bootstrap CSS -->
    <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css" rel="stylesheet">
    <link href="style.css" rel="stylesheet"></link>
</head>

<body class="container">

  <div class="navbar">
    <div class="navbar-inner">
      <a class="brand" ui-sref="index">Sub-State<br>Example</a>
      <ul class="nav">
        <li><a href="#/slowcart">delayed cart</a></li>
        <li><a ui-sref="index.withOptions({number: null, size: null})">Home</a></li>
        <li><a ui-sref="index.withOptions({number: 0, size: 4})">Page 0 Size 4</a></li>
        <li><a ui-sref="index.withOptions({number: 1, size: 4})">Page 1 Size 4</a></li>
        <li><a ui-sref="index.withOptions({number: 2, size: 4})">Page 2 Size 4</a></li>
        <li><a ui-sref="index.withOptions({number: 0, size: 8})">Page 0 Size 8</a></li>
        <li><a ui-sref="index.withOptions({number: 1, size: 8})">Page 1 Size 8</a></li>
        <li><a ui-sref="index.withOptions({number: 2, size: 8})">Page 2 Size 8</a></li>
        <li><a ui-sref="index2">Other Page</a></li>
      </ul>
    </div>
  </div>

  
  <div class="row" style='padding: 5px;'>
    <div class="span6">
      <div class="well" ui-view="table"></div>        
    </div>
  </div>         
  
  <!-- Angular -->
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js"></script>
  <!-- UI-Router -->
  <script src="//angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
  <script src="bindonce.js" type="text/javascript"></script>
  <!-- App Script -->
  <script src="app.js" type="text/javascript">
  </script>

</body>

</html>
/* Put your css in here */

.navbar .nav li{
  float: none;
}
index.viewA

<div>Page Number:{{pageNumber || "default"}}</div>
<div>Page Size: {{size || "default"}}</div>
<div>Last Updated: {{date()}}</div> 
<!-- Using Bindonce to identify the initial timestamp the template was rendered at -->
Rendered at: <span bindonce bo-text="{{date()}}"></span>

<div style="border: solid black 1px;">
<div data-ng-repeat="row in rows | startFrom:(pageNumber * size || 0) | limitTo:(size || 100) ">
  <span>{{row.number}}</span>
  <span>{{row.description}}</span>
</div>
<div>
  <a data-ui-sref='index.withOptions({number:getPreviousPage()})'>&lt;</a>
  &nbsp;&nbsp;
  <a  data-ui-sref='index.withOptions({number:getNextPage()})'>&gt;</a>
</div>
</div>
(function () {
	"use strict";
	/**
	 * Bindonce - Zero watches binding for AngularJs
	 * @version v0.3.1
	 * @link https://github.com/Pasvaz/bindonce
	 * @author Pasquale Vazzana <pasqualevazzana@gmail.com>
	 * @license MIT License, http://www.opensource.org/licenses/MIT
	 */

	var bindonceModule = angular.module('pasvaz.bindonce', []);

	bindonceModule.directive('bindonce', function ()
	{
		var toBoolean = function (value)
		{
			if (value && value.length !== 0)
			{
				var v = angular.lowercase("" + value);
				value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
			}
			else
			{
				value = false;
			}
			return value;
		};

		var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
		if (isNaN(msie))
		{
			msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
		}

		var bindonceDirective =
		{
			restrict: "AM",
			controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
			{
				var showHideBinder = function (elm, attr, value)
				{
					var show = (attr === 'show') ? '' : 'none';
					var hide = (attr === 'hide') ? '' : 'none';
					elm.css('display', toBoolean(value) ? show : hide);
				};
				var classBinder = function (elm, value)
				{
					if (angular.isObject(value) && !angular.isArray(value))
					{
						var results = [];
						angular.forEach(value, function (value, index)
						{
							if (value) results.push(index);
						});
						value = results;
					}
					if (value)
					{
						elm.addClass(angular.isArray(value) ? value.join(' ') : value);
					}
				};
				var transclude = function (transcluder, scope)
				{
					transcluder.transclude(scope, function (clone)
					{
						var parent = transcluder.element.parent();
						var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
						var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
						var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
						angular.forEach(clone, function (node)
						{
							parentNode.insertBefore(node, afterNextSibling);
						});
					});
				};

				var ctrl =
				{
					watcherRemover: undefined,
					binders: [],
					group: $attrs.boName,
					element: $element,
					ran: false,

					addBinder: function (binder)
					{
						this.binders.push(binder);

						// In case of late binding (when using the directive bo-name/bo-parent)
						// it happens only when you use nested bindonce, if the bo-children
						// are not dom children the linking can follow another order
						if (this.ran)
						{
							this.runBinders();
						}
					},

					setupWatcher: function (bindonceValue)
					{
						var that = this;
						this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
						{
							if (newValue === undefined) return;
							that.removeWatcher();
							that.checkBindonce(newValue);
						}, true);
					},

					checkBindonce: function (value)
					{
						var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
						// since Angular 1.2 promises are no longer 
						// undefined until they don't get resolved
						if (typeof promise === 'function')
						{
							promise(function ()
							{
								that.runBinders();
							});
						}
						else
						{
							that.runBinders();
						}
					},

					removeWatcher: function ()
					{
						if (this.watcherRemover !== undefined)
						{
							this.watcherRemover();
							this.watcherRemover = undefined;
						}
					},

					runBinders: function ()
					{
						while (this.binders.length > 0)
						{
							var binder = this.binders.shift();
							if (this.group && this.group != binder.group) continue;
							var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
							switch (binder.attr)
							{
								case 'boIf':
									if (toBoolean(value))
									{
										transclude(binder, binder.scope.$new());
									}
									break;
								case 'boSwitch':
									var selectedTranscludes, switchCtrl = binder.controller[0];
									if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
									{
										binder.scope.$eval(binder.attrs.change);
										angular.forEach(selectedTranscludes, function (selectedTransclude)
										{
											transclude(selectedTransclude, binder.scope.$new());
										});
									}
									break;
								case 'boSwitchWhen':
									var ctrl = binder.controller[0];
									ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
									ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
									break;
								case 'boSwitchDefault':
									var ctrl = binder.controller[0];
									ctrl.cases['?'] = (ctrl.cases['?'] || []);
									ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
									break;
								case 'hide':
								case 'show':
									showHideBinder(binder.element, binder.attr, value);
									break;
								case 'class':
									classBinder(binder.element, value);
									break;
								case 'text':
									binder.element.text(value);
									break;
								case 'html':
									binder.element.html(value);
									break;
								case 'style':
									binder.element.css(value);
									break;
								case 'src':
									binder.element.attr(binder.attr, value);
									if (msie) binder.element.prop('src', value);
									break;
								case 'attr':
									angular.forEach(binder.attrs, function (attrValue, attrKey)
									{
										var newAttr, newValue;
										if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
										{
											newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
											newValue = binder.scope.$eval(binder.attrs[attrKey]);
											binder.element.attr(newAttr, newValue);
										}
									});
									break;
								case 'href':
								case 'alt':
								case 'title':
								case 'id':
								case 'value':
									binder.element.attr(binder.attr, value);
									break;
							}
						}
						this.ran = true;
					}
				};

				return ctrl;
			}],

			link: function (scope, elm, attrs, bindonceController)
			{
				var value = attrs.bindonce && scope.$eval(attrs.bindonce);
				if (value !== undefined)
				{
					bindonceController.checkBindonce(value);
				}
				else
				{
					bindonceController.setupWatcher(attrs.bindonce);
					elm.bind("$destroy", bindonceController.removeWatcher);
				}
			}
		};

		return bindonceDirective;
	});

	angular.forEach(
	[
		{ directiveName: 'boShow', attribute: 'show' },
		{ directiveName: 'boHide', attribute: 'hide' },
		{ directiveName: 'boClass', attribute: 'class' },
		{ directiveName: 'boText', attribute: 'text' },
		{ directiveName: 'boBind', attribute: 'text' },
		{ directiveName: 'boHtml', attribute: 'html' },
		{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
		{ directiveName: 'boSrc', attribute: 'src' },
		{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
		{ directiveName: 'boHref', attribute: 'href' },
		{ directiveName: 'boAlt', attribute: 'alt' },
		{ directiveName: 'boTitle', attribute: 'title' },
		{ directiveName: 'boId', attribute: 'id' },
		{ directiveName: 'boStyle', attribute: 'style' },
		{ directiveName: 'boValue', attribute: 'value' },
		{ directiveName: 'boAttr', attribute: 'attr' },

		{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
		{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
		{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
		{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
	],
	function (boDirective)
	{
		var childPriority = 200;
		return bindonceModule.directive(boDirective.directiveName, function ()
		{
			var bindonceDirective =
			{
				priority: boDirective.priority || childPriority,
				transclude: boDirective.transclude || false,
				terminal: boDirective.terminal || false,
				require: ['^bindonce'].concat(boDirective.require || []),
				controller: boDirective.controller,
				compile: function (tElement, tAttrs, transclude)
				{
					return function (scope, elm, attrs, controllers)
					{
						var bindonceController = controllers[0];
						var name = attrs.boParent;
						if (name && bindonceController.group !== name)
						{
							var element = bindonceController.element.parent();
							bindonceController = undefined;
							var parentValue;

							while (element[0].nodeType !== 9 && element.length)
							{
								if ((parentValue = element.data('$bindonceController'))
									&& parentValue.group === name)
								{
									bindonceController = parentValue;
									break;
								}
								element = element.parent();
							}
							if (!bindonceController)
							{
								throw new Error("No bindonce controller: " + name);
							}
						}

						bindonceController.addBinder(
						{
							element: elm,
							attr: boDirective.attribute || boDirective.directiveName,
							attrs: attrs,
							value: attrs[boDirective.directiveName],
							interpolate: boDirective.interpolate,
							group: name,
							transclude: transclude,
							controller: controllers.slice(1),
							scope: scope
						});
					};
				}
			};

			return bindonceDirective;
		});
	})
})();
This should be blank. It is simply verifying that the state did not inherit the number/size
parameters from the navigated from state.<br><br>
Page Number: {{index2Number}}