Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created September 4, 2013 13:42
Show Gist options
  • Save bennadel/6437102 to your computer and use it in GitHub Desktop.
Save bennadel/6437102 to your computer and use it in GitHub Desktop.
Using Base Controllers In AngularJS - An Experiment
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>
Using Base Controllers In AngularJS
</title>
</head>
<body ng-controller="demo.SubController">
<h1>
{{ title }}
</h1>
<p>
Foo: {{ foo }}.
</p>
<p>
Bar: {{ bar }}.
</p>
<!-- Load jQuery and AngularJS from the CDN. -->
<script
type="text/javascript"
src="//code.jquery.com/jquery-2.0.3.min.js">
</script>
<script
type="text/javascript"
src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js">
</script>
<script type="text/javascript">
// Create an application module for our demo.
var app = angular.module( "Demo", [] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// Set up the Controller-Factory. This is not a feature that
// AngularJS provides out of the box. As such, we have to
// jerry-rig our own factory using the core factory.
(function( core, coreFactory ) {
// Controllers will be defined by dot-delimited namespaces
// that end in "Controller" (ex. foo.BarController).
var pattern = /\.[^.]*?Controller$/i;
// As the factories are invoked, each will return the
// constructor for the given Controller; we can cache these
// so we don't have to keep re-wiring the factories.
var constructors = {};
// I proxy the core factory and route the request to either
// the Controller provider or the underlying factory.
function factory( name, controllerFactory ) {
// If the given injectable name is not one of our
// factories, then just hand it off to the core
// factory registration.
if ( ! pattern.test( name ) ) {
return(
coreFactory.apply( core, arguments )
);
}
// Register the Controller Factory method as a
// Controller. Here, we will leverage the fact that
// the *RETURN* value of the constructor is what is
// actually being used as the Controller instance.
core.controller(
name,
function( $scope, $injector ) {
var cacheKey = ( "cache_" + name );
var Constructor = constructors[ cacheKey ];
// If the cached constructor hasn't been built
// yet, invoke the factory and cache the
// constructor for later use.
if ( ! Constructor ) {
Constructor
= constructors[ cacheKey ]
= $injector.invoke( controllerFactory )
;
}
// By returning something other than _this_,
// we are telling AngularJS to use the following
// object instance as the Controller instead of
// the of the current context (ie, the Factory).
// --
// NOTE: We have to pass $scope through as an
// injectable otherwise the Dependency-Injection
// framework will not know how to create it.
return(
$injector.instantiate(
Constructor,
{
"$scope": $scope
}
)
);
}
);
// Return the core to continue method chaining.
return( core );
};
// Overwrite the Angular-provided factory.
core.factory = factory;
})( app, app.factory );
// -------------------------------------------------- //
// -------------------------------------------------- //
// Define the base-controller; since this is not a name-spaced
// controller, it will be routed to the underlying, core
// factory method.
app.factory(
"BaseController",
function() {
function BaseController( $scope ) {
return( this );
}
BaseController.prototype = {
getFoo: function() {
return( "Foo ( from BaseController )" );
}
};
return( BaseController );
}
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// Define the "sub-class" controller; since this is a name-
// spaced controller, it will be routed to the wrapper factory
// that will proxy the controller instantiation.
app.factory(
"demo.SubController",
function( BaseController, $document ) {
function SubController( $scope, $document ) {
BaseController.call( this, $scope );
// Store foo/bar for use in the View-model.
$scope.foo = this.getFoo();
$scope.bar = this.getBar();
// Add some other injectables, just to make sure
// the factory wrapper didn't screw up the
// dependency-injection framework.
$scope.title = $document[ 0 ].title;
}
// Extend the base controller.
SubController.prototype = Object.create( BaseController.prototype );
// Add sub-class methods.
SubController.prototype.getBar = function() {
return( "Bar ( from SubController )" );
};
// Override base method; decorate the value provided
// by the super-class.
SubController.prototype.getFoo = function() {
return(
BaseController.prototype.getFoo.call( this ) +
"( overridden by SubClass )"
);
};
return( SubController );
}
);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment