In general in my Angular / TypeScript apps I've tended to use the "controller as" syntax.
However, for a CRUD screen I'm currently working on I've found myself diverting away from this. So that I can take advantage of $watchCollection
/ checking for changes etc I've found myself using the pattern advised by that excellent chap @basarat here. Namely, in the video (which pre-dates "controller as" syntax in Angular) @basarat creates a variable representing the controller on the $scope
called vm
. And in the same way as with using a "controller as vm" in the view you can still use the vm.myProperty
/ vm.myFunction()
style syntax to interact with your model.
So, the controller looks like this:
module controllers {
"use strict";
interface sageEditRouteParams extends ng.route.IRouteParamsService {
id: number;
}
interface sageEditScope extends ng.IScope {
vm: SageEdit;
}
class SageEdit {
log: loggerFunction;
sage: sage;
title: string;
private _hasChanges: boolean;
static $inject = ["$routeParams", "$scope", "common", "datacontext"];
constructor(
private $routeParams: sageEditRouteParams,
private $scope: sageEditScope,
private common: common,
private datacontext: datacontext
) {
this.sage = undefined;
this.title = "Sage Edit";
this.log = common.logger.getLogFn(controllerId);
$scope.vm = this;
$scope.$watchCollection("vm.sage", (newSage: sage, oldSage: sage) => {
if (newSage && oldSage) {
this._hasChanges = true;
}
});
this.activate();
}
// Prototype methods
activate() {
var id = this.$routeParams.id;
var dataPromises: ng.IPromise<any>[] = [this.getSage(id)];
this.common.activateController(dataPromises, controllerId)
.then(() => {
this.log("Activated Sage Edit View");
this.title = "Sage Edit: " + this.sage.name;
});
}
getSage(id: number) {
return this.datacontext.sage.getById(id).then(data => {
this.sage = data;
this._hasChanges = false;
});
}
get hasChanges(): boolean {
return this._hasChanges;
}
}
var controllerId = "sageEdit";
angular.module("app").controller(controllerId, SageEdit);
}
And the view like this:
<section class="mainbar" ng-controller="sageEdit">
<section class="matter">
<div class="container-fluid">
<div>
<button class="btn btn-info"
ng-click="vm.cancel()"
ng-disabled="!vm.canSave">
<i class="fa fa-undo"></i>Cancel
</button>
<button class="btn btn-info"
ng-click="vm.save()"
ng-disabled="!vm.canSave">
<i class="glyphicon glyphicon-save"></i>Save
</button>
<span ng-show="vm.hasChanges"
class="dissolve-animation ng-hide">
<i class="glyphicon glyphicon-asterisk text-info"></i>
</span>
</div>
<div class="widget wgreen">
<div data-cc-widget-header title="{{vm.title}}"></div>
<div class="widget-content form-horizontal">
<div class="form-group">
<label class="col-xs-12 col-sm-2">Name</label>
<input class="col-xs-12 col-sm-9" ng-model="vm.sage.name" />
</div>
<div class="form-group">
<label class="col-xs-12 col-sm-2">Username</label>
<input class="col-xs-12 col-sm-9" ng-model="vm.sage.userName" />
</div>
<div class="form-group">
<label class="col-xs-12 col-sm-2">Email</label>
<input class="col-xs-12 col-sm-9"
type="email"
ng-model="vm.sage.email" />
</div>
</div>
</div>
</div>
</section>
</section>
What I'm putting together seems to work just fine but I wanted to put it out there and get some other views. Are there:
I've done a little digging but found not anything conclusive.
For
$scope.$watchCollection("vm.sage", (newSage: sage, oldSage: sage) => {
You can also do for refactoring ease:
$scope.$watchCollection(()=>this.sage, (newSage: sage, oldSage: sage) => {
In certain scenarios when you try to watch something like foo.bar
where foo
is still undefined you can use a safe wrapper function safeWatch(()=>this.foo.bar)
:
function safeWatch<T extends Function>(expression: T) {
return () => {
try {
return expression();
}
catch (e) {
return null;
}
};
}
Fron source code: https://github.com/angular/angular.js/blob/36831eccd1da37c089f2141a2c073a6db69f3e1d/src/ng/controller.js#L95
That is exactly what angular is doing for you. i.e. $scope.vm = instance
(where vm == indentifier
) So they are equivalent
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With