挖掘angularjs "controller as" 语法

原文:http://toddmotto.com/digging-into-angulars-controller-as-syntax/

AngularJS Controllers have recently gone under some changes (version 1.2 to be precise). What this means for scopes, Controllers and Angular development is some very subtle but powerful changes. One of those changes I believe is improved architecture, clearer scoping and smarter Controllers.

Controllers as we know them are class-like Objects that drive Model and View changes, but they all seem to revolve around this mystical $scope Object. Angular Controllers have been pushed to change the way $scope is declared, with many developers suggesting using the this keyword instead of $scope.

Pre v1.2.0 Controllers looked similar to this:

// <div ng-controller="MainCtrl"></div>
app.controller(‘MainCtrl‘, function ($scope) {
  $scope.title = ‘Some title‘;
});

Here, the concept of the Controller is separate from the $scope itself, as we have to dependency inject it. Some argued this would‘ve been better:

app.controller(‘MainCtrl‘, function () {
  this.title = ‘Some title‘;
});

We didn‘t quite get there, but we got something pretty awesome in return.

Controllers as Classes

If you instantiate a "class" in JavaScript, you might do this:

var myClass = function () {
  this.title = ‘Class title‘;
}
var myInstance = new myClass();

We can then use the myInstance instance to access myClass methods and properties. In Angular, we get the feel of proper instantiation with the new Controller as syntax. Here‘s a quick look at declaring and binding:

// we declare as usual, just using the `this` Object instead of `$scope`
app.controller(‘MainCtrl‘, function () {
  this.title = ‘Some title‘;
});

This is more of a class based setup, and when instantiating a Controller in the DOM we get to instantiate against a variable:

<div ng-controller="MainCtrl as main">
  // MainCtrl doesn‘t exist, we get the `main` instance only
</div>

To reflect this.title in the DOM, we need to ride off our instance:

<div ng-controller="MainCtrl as main">
   {{ main.title }}
</div>

Namespacing the scopes is a great move I think, it cleans up Angular massively. I‘ve always disliked the "floating variables" such as {{ title }}, I much prefer hitting the instance with {{ main.title }}.

Nested scopes

Nested scopes is where we see great return from the Controller as syntax, often we‘ve had to use the current scope‘s $parent property to scale back up scopes to get where we need.

Take this for example:

<div ng-controller="MainCtrl">
  {{ title }}
  <div ng-controller="AnotherCtrl">
    {{ title }}
    <div ng-controller="YetAnotherCtrl">
      {{ title }}
    </div>
  </div>
</div>

Firstly, we‘re going to get interpolation issues as {{ title }} will be very confusing to use and most likely one scope will take precidence over another. We also don‘t know which one that might be. Whereas if we did this things are far clearer and variables can be accessed properly across scopes:

<div ng-controller="MainCtrl as main">
  {{ main.title }}
  <div ng-controller="AnotherCtrl as another">
    {{ another.title }}
    <div ng-controller="YetAnotherCtrl as yet">
      {{ yet.title }}
    </div>
  </div>
</div>

I can also access parent scopes without doing this:

<div ng-controller="MainCtrl">
  {{ title }}
  <div ng-controller="AnotherCtrl">
    Scope title: {{ title }}
    Parent title: {{ $parent.title }}
    <div ng-controller="YetAnotherCtrl">
      {{ title }}
      Parent title: {{ $parent.title }}
      Parent parent title: {{ $parent.$parent.title }}
    </div>
  </div>
</div>

And make things more logical:

<div ng-controller="MainCtrl as main">
  {{ main.title }}
  <div ng-controller="AnotherCtrl as another">
    Scope title: {{ another.title }}
    Parent title: {{ main.title }}
    <div ng-controller="YetAnotherCtrl as yet">
      Scope title: {{ yet.title }}
      Parent title: {{ another.title }}
      Parent parent title: {{ main.title }}
    </div>
  </div>
</div>

No hacky $parent calls. If a Controller‘s position in the DOM/stack were to change, the position in sequential $parent.$parent.$parent.$parent may change! Accessing the scope lexically makes perfect sense.

$watchers/$scope methods

The first time I used the Controller as syntax I was like "yeah, awesome!", but then to use scope watchers or methods (such as $watch$broadcast$on etc.) we need to dependency inject$scope. Gargh, this is what we tried so hard to get away from. But then I realised this was awesome.

The way the Controller as syntax works, is by binding the Controller to the current $scope rather than it being all one $scope-like class-like Object. For me, the key is the separation between the class and special Angular features.

This means I can have my pretty class-like Controller:

app.controller(‘MainCtrl‘, function () {
  this.title = ‘Some title‘;
});

When I need something above and beyond generic bindings, I introduce the magnificent $scopedependency to do something special, rather than ordinary.

Those special things include all the $scope methods, let‘s look at an example:

app.controller(‘MainCtrl‘, function ($scope) {
  this.title = ‘Some title‘;
  $scope.$on(‘someEventFiredFromElsewhere‘, function (event, data) {
    // do something!
  });
});

Ironing a quirk

Interestingly enough, whilst writing this I wanted to provide a $scope.$watch() example. Doing this usually is very simple, but using the Controller as syntax doesn‘t work quite as expected:

app.controller(‘MainCtrl‘, function ($scope) {
  this.title = ‘Some title‘;
  // doesn‘t work!
  $scope.$watch(‘title‘, function (newVal, oldVal) {});
  // doens‘t work!
  $scope.$watch(‘this.title‘, function (newVal, oldVal) {});
});

Uh oh! So what do we do? Interestingly enough I was reading the other day, and you can actually pass in a function as the first argument of a $watch():

app.controller(‘MainCtrl‘, function ($scope) {
  this.title = ‘Some title‘;
  // hmmm, a function
  $scope.$watch(function () {}, function (newVal, oldVal) {});
});

Which means we can return our this.title reference:

app.controller(‘MainCtrl‘, function ($scope) {
  this.title = ‘Some title‘;
  // nearly there...
  $scope.$watch(function () {
    return this.title; // `this` isn‘t the `this` above!!
  }, function (newVal, oldVal) {});
});

Let‘s change some execution context using angular.bind():

app.controller(‘MainCtrl‘, function ($scope) {
  this.title = ‘Some title‘;
  // boom
  $scope.$watch(angular.bind(this, function () {
    return this.title; // `this` IS the `this` above!!
  }), function (newVal, oldVal) {
    // now we will pickup changes to newVal and oldVal
  });
});

Declaring in $routeProvider/Directives/elsewhere

Controllers can by dynamically assigned, we don‘t need to always bind them via attributes. Inside Directives, we get a controllerAs: property, this is easily assigned:

app.directive(‘myDirective‘, function () {
  return {
    restrict: ‘EA‘,
    replace: true,
    scope: true,
    template: [].join(‘‘),
    controllerAs: ‘‘, // woohoo, nice and easy!
    controller: function () {}, // we‘ll instantiate this controller "as" the above name
    link: function () {}
  };
});

The same inside $routeProvider:

app.config(function ($routeProvider) {
  $routeProvider
  .when(‘/‘, {
    templateUrl: ‘views/main.html‘,
    controllerAs: ‘‘,
    controller: ‘‘
  })
  .otherwise({
    redirectTo: ‘/‘
  });
});
时间: 2024-10-27 00:34:03

挖掘angularjs "controller as" 语法的相关文章

(十六)JQuery Ready和angularJS controller的运行顺序问题

项目中使用了JQuery和AngularJS框架,近期定位一个问题,原因就是JQuery Ready写在了angularJS controller之前,导致JQuery选择器无法选中须要的元素(由于angularJS controller还没有初始化,dom元素的class属性没有被加入).于是就引出了一个问题,jquery和angularjs谁先运行谁后运行的问题.当然最好我们编写的代码不要依赖于这样的顺序,依赖于某些顺序的代码更easy出错. <html> <head> <

JQuery Ready和angularJS controller的执行顺序问题

项目中使用了JQuery和AngularJS框架,最近定位一个问题,原因就是JQuery Ready写在了angularJS controller之前,导致JQuery选择器无法选中需要的元素(因为angularJS controller还没有初始化,dom元素的class属性没有被添加).于是就引出了一个问题,jquery和angularjs谁先执行谁后执行的问题.当然最好我们编写的代码不要依赖于这种顺序,依赖于某些顺序的代码更容易出错. <html> <head> <sc

Angularjs Controller

Angularjs Controller 使用过程中的注意点:1.不要试图去复用 Controller, 一个控制器一般只负责一小块试图.2.不要在Controller 中操作DOM, 这不是控制器的职责.3.不要在Controller 里面数据格式化,ng 有很好用的表单空间4. 不要在Controller 里面对数据进行过滤,ng 有$filter 服务5. Controller 是不会互相调用的,控制器之间的交互会通过事件进行.

【转】Angularjs Controller 间通信机制

在Angularjs开发一些经验总结随笔中提到我们需要按照业务却分angular controller,避免过大无所不能的上帝controller,我们把controller分离开了,但是有时候我们需要在controller中通信,一般为比较简单的通信机制,告诉同伴controller我的某个你所关心的东西改变了,怎么办?如果你是一个javascript程序员你会很自然的想到异步回调响应式通信—事件机制(或消息机制).对,这就是angularjs解决controller之间通信的机制,所推荐的唯

跟我学AngularJs:Controller数据共享、继承、通信使用具体解释

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主讲了AngularJs中的Controller中数据共享.继承.通信的具体使用 本教程使用AngularJs版本号:1.5.3 AngularJs GitHub: https://github.com/angular/angular.js/ AngularJs下载地址:https://angularjs.org/ 一.controller基础与使用方法 AngularJS中的co

AngularJS(三)——在多个AngularJS controller之间共享数据

在MVC中,为了方便维护,针对不同业务会使用不同的controller.有时我们需要在不同的controller中共享数据,本文旨在解决这一问题. 1. 使用$rootScope直接绑定 AngularJS中有一个$rootScope对象,它是AngularJS中最接近全局作用域的对象,是所有$scope对象的最上层,可以简单理解为BOM中的window对象或Node.js中的global对象.最简单的方式是直接将要共享的数据绑定到$rootScope对象中: <!DOCTYPE html>

跟我学AngularJs:Controller数据共享、继承、通信使用详解

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主讲了AngularJs中的Controller中数据共享.继承.通信的详细使用 本教程使用AngularJs版本:1.5.3 AngularJs GitHub: https://github.com/angular/angular.js/ AngularJs下载地址:https://angularjs.org/ 一.controller基础与用法 AngularJS中的contr

angularjs controller间的传值方法

AngularJS中的controller是个函数,用来向视图的作用域($scope)添加额外的功能,我们用它来给作用域对象设置初始状态,并添加自定义行为. 当我们在创建新的控制器时,angularJS会帮我们生成并传递一个新的$scope对象给这个controller,在angularJS应用的中的任何一个部分,都有父级作用域的存在,顶级就是ng-app所在的层级,它的父级作用域就是$rootScope. 每个$scope的$root指向$rootScope, $cope.$parent指向父

angularjs controller的两种写法

在Angular中,Directive.Service.Filter.Controller都是以工厂方法的方式给出,而工厂方法的参数名对应着该工厂方法依赖的Service.如: app.controller('wolrdCtrl', function($scope, $http){     // ... }); 在上述的function执行之前,Angular Injector会生成一个$scope的实例和$http的实例,并传入该方法. 如果你希望对JS进行压缩处理,那么参数名就可能发生变化,