AngularJS 控制器(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在 AngularJS 的架构中,控制器如同一座连接数据与视图的桥梁,它负责管理应用的状态、逻辑和交互。无论是初学者还是中级开发者,掌握控制器的使用方法,是构建复杂单页应用(SPA)的关键一步。本文将从基础概念出发,逐步深入讲解控制器的原理、作用域、依赖注入,以及如何通过实际案例实现功能。


什么是 AngularJS 控制器?

AngularJS 控制器是一个 JavaScript 函数,用于初始化和管理 AngularJS 应用中的特定模块或视图。它通过绑定数据、响应用户事件、调用服务等方式,实现视图与模型的交互。

控制器的核心作用

  1. 数据绑定:将数据传递到视图中,或从视图接收用户输入。
  2. 逻辑处理:执行业务逻辑,例如表单验证、数据计算或异步请求。
  3. 服务集成:通过依赖注入(DI)调用 AngularJS 内置或自定义的服务。

形象比喻

可以将控制器想象为“舞台导演”——它不直接参与表演(视图),但负责安排演员(数据)的位置、调整灯光(逻辑),并协调后台团队(服务)的工作。


如何创建一个控制器?

基本语法

控制器通过 AngularJS 的 controller() 方法或 ng-controller 指令定义。以下是两种常见方式:

方法一:模块注册方式

// 定义一个名为 "TodoController" 的控制器  
angular.module('myApp', [])  
  .controller('TodoController', function($scope) {  
    $scope.tasks = ['Learn AngularJS', 'Build a Todo App'];  
    $scope.addTask = function(newTask) {  
      $scope.tasks.push(newTask);  
    };  
  });  

方法二:直接绑定到 HTML

<div ng-controller="TodoController">  
  <input type="text" ng-model="newTask">  
  <button ng-click="addTask(newTask)">Add</button>  
  <ul>  
    <li ng-repeat="task in tasks">{{ task }}</li>  
  </ul>  
</div>  

关键点解析

  • $scope 是控制器与视图通信的纽带,它存储数据和方法。
  • 通过 ng-controller 指令将控制器绑定到 HTML 元素,该元素及其子元素均可访问 $scope 中的属性。

控制器与作用域(Scope)的关系

作用域是 AngularJS 中的核心概念,它负责在控制器和视图之间传递数据。

作用域的层级结构

作用域具有父子层级关系,例如:

// 父控制器  
app.controller('ParentCtrl', function($scope) {  
  $scope.message = 'Hello from Parent!';  
});  

// 子控制器继承父作用域  
app.controller('ChildCtrl', function($scope) {  
  $scope.message = 'Hello from Child!'; // 覆盖父作用域的值  
});  

图形化比喻

作用域层级如同“家庭关系”——子作用域可以继承父作用域的属性,但也可以定义自己的属性,形成独立的“房间”。


控制器的依赖注入(Dependency Injection)

AngularJS 的依赖注入(DI)机制允许控制器动态引入服务、工厂或其他模块,从而实现代码的模块化和复用。

基本语法

// 引入 $http 服务  
app.controller('UserController', function($scope, $http) {  
  $http.get('/api/users')  
    .then(function(response) {  
      $scope.users = response.data;  
    });  
});  

依赖注入的两种写法

  1. 内联数组注解(推荐):

    app.controller('UserController', ['$scope', '$http', function($scope, $http) {  
      // ...  
    }]);  
    

    这种写法在代码压缩后仍能保留依赖名称,避免错误。

  2. 隐式注入(不推荐):

    app.controller('UserController', function($scope, $http) {  
      // ...  
    });  
    

    若代码被压缩(如 function(a,b)),AngularJS 将无法识别依赖名称,导致错误。


控制器的高级用法

1. 控制器继承与作用域隔离

通过 $controller 服务,控制器可以继承其他控制器的逻辑:

// 父控制器  
app.controller('BaseCtrl', function($scope) {  
  $scope.greet = function() {  
    return 'Hello!';  
  };  
});  

// 子控制器继承 BaseCtrl  
app.controller('ChildCtrl', ['$scope', '$controller', function($scope, $controller) {  
  $controller('BaseCtrl', { $scope: $scope }); // 继承父作用域  
  $scope.message = 'I am the child!';  
}]);  

2. 通过事件进行控制器通信

使用 $emit$on 方法,控制器可以在不同作用域层级间传递消息:

// 父控制器  
app.controller('ParentCtrl', function($scope) {  
  $scope.$on('child-event', function(event, data) {  
    console.log('Received:', data); // 输出 "Hello from Child!"  
  });  
});  

// 子控制器  
app.controller('ChildCtrl', function($scope) {  
  $scope.sendMessage = function() {  
    $scope.$emit('child-event', 'Hello from Child!');  
  };  
});  

实战案例:构建一个待办事项应用

需求

实现一个简单的待办事项列表,包含以下功能:

  • 添加任务
  • 标记任务为已完成
  • 删除任务

实现步骤

1. 定义控制器

app.controller('TodoCtrl', ['$scope', function($scope) {  
  // 初始化数据  
  $scope.todos = [  
    { text: 'Learn AngularJS Controllers', done: false },  
    { text: 'Write a Blog Post', done: true }  
  ];  

  // 添加任务  
  $scope.addTodo = function() {  
    if ($scope.newTodo.trim() === '') return;  
    $scope.todos.push({ text: $scope.newTodo, done: false });  
    $scope.newTodo = ''; // 清空输入框  
  };  

  // 删除任务  
  $scope.removeTodo = function(index) {  
    $scope.todos.splice(index, 1);  
  };  
}]);  

2. HTML 视图绑定

<div ng-controller="TodoCtrl">  
  <input type="text" ng-model="newTodo" placeholder="Add a new task">  
  <button ng-click="addTodo()">Add</button>  

  <ul>  
    <li ng-repeat="todo in todos">  
      <input type="checkbox" ng-model="todo.done">  
      <span ng-class="{ done: todo.done }">{{ todo.text }}</span>  
      <button ng-click="removeTodo($index)">Delete</button>  
    </li>  
  </ul>  
</div>  

3. 样式增强(可选)

.done {  
  text-decoration: line-through;  
  color: #888;  
}  

功能解析

  • 数据绑定:通过 ng-model 实现输入框与 $scope.newTodo 的双向绑定。
  • 事件触发ng-click 触发 addTodoremoveTodo 方法。
  • 列表渲染ng-repeat 遍历 $scope.todos,并动态生成列表项。

控制器的最佳实践

1. 避免在控制器中放置复杂逻辑

控制器应专注于协调数据与视图,复杂的业务逻辑应放入服务或工厂中。例如:

// 不推荐:控制器直接处理数据验证  
$scope.saveUser = function(user) {  
  if (!user.email) return alert('Email is required!');  
  // ...  
};  

// 推荐:将验证逻辑放入服务  
app.service('UserService', function() {  
  this.validateUser = function(user) {  
    return user.email ? true : false;  
  };  
});  

app.controller('UserController', ['$scope', 'UserService', function($scope, UserService) {  
  $scope.saveUser = function(user) {  
    if (!UserService.validateUser(user)) return alert('Email is required!');  
    // ...  
  };  
}]);  

2. 使用 controllerAs 语法

通过 controllerAs 提升代码可读性,避免 $scope 的过度使用:

// 定义控制器  
app.controller('TodoCtrl', function() {  
  this.todos = []; // 使用 this 替代 $scope  
  this.addTodo = function() {  
    // ...  
  };  
});  
<!-- 绑定到 vm(ViewModel)变量 -->  
<div ng-controller="TodoCtrl as vm">  
  <input ng-model="vm.newTodo">  
  <button ng-click="vm.addTodo()">Add</button>  
  <ul>  
    <li ng-repeat="todo in vm.todos">{{ todo.text }}</li>  
  </ul>  
</div>  

3. 控制器生命周期钩子

AngularJS 提供了 constructor$onInit 等生命周期钩子,用于初始化或响应依赖注入的变化:

app.controller('LifecycleCtrl', function() {  
  // 构造函数  
  this.init = function() {  
    console.log('Controller initialized');  
  };  

  // AngularJS 1.5+ 生命周期钩子  
  this.$onInit = function() {  
    this.init();  
  };  
});  

结论

AngularJS 控制器是构建动态 Web 应用的核心组件,它通过作用域与视图通信,借助依赖注入整合服务,并通过事件和继承实现复杂交互。掌握控制器的设计模式和最佳实践,可以帮助开发者高效地构建可维护、可扩展的 AngularJS 应用。

从基础语法到高级技巧,控制器的每一个细节都值得深入探索。无论是管理数据、协调服务,还是优化代码结构,控制器始终是连接 AngularJS 生态的“神经中枢”。通过不断实践和优化,开发者可以充分利用控制器的潜力,创造出令人惊叹的前端应用。


希望这篇文章能为你的 AngularJS 学习之旅提供清晰的指引!如果遇到问题,欢迎在评论区交流。

最新发布