AngularJS仪表盘路由实例:模块化设计

AngularJS仪表盘路由实例:模块化设计

【免费下载链接】ui-router The de-facto solution to flexible routing with nested views in AngularJS 【免费下载链接】ui-router 项目地址: https://gitcode.com/gh_mirrors/ui/ui-router

你是否在开发AngularJS应用时遇到过路由混乱、视图嵌套复杂的问题?本文将通过一个仪表盘实例,展示如何使用ui-router实现模块化路由设计,让你的应用结构更清晰、维护更简单。读完本文,你将掌握:如何设计嵌套路由结构、使用命名视图实现复杂布局、通过resolve解决数据依赖,以及如何优化路由性能。

为什么选择ui-router?

AngularJS自带的ngRoute模块仅支持平面路由,无法满足复杂应用的需求。而ui-router作为AngularJS生态中事实上的路由解决方案(The de-facto solution to flexible routing with nested views in AngularJS),提供了更强大的功能:

  • 支持嵌套视图(Nested Views)
  • 允许命名视图(Named Views)
  • 提供状态机管理(State Management)
  • 支持复杂的路由参数和解析(Resolve)

项目核心的状态管理功能由src/stateProvider.ts实现,而视图渲染则通过src/directives/viewDirective.ts中的ui-view指令完成。

仪表盘应用路由设计

我们将构建一个包含以下功能的仪表盘应用:

  • 顶部导航栏(固定)
  • 侧边菜单(可折叠)
  • 主内容区(根据路由动态变化)
  • 数据统计卡片(复用组件)

路由结构设计

使用mermaid展示我们的路由结构:

mermaid

这个结构中,dashboard是抽象状态(abstract),作为其他子状态的父状态,负责布局框架的渲染。

实现步骤

1. 引入ui-router

首先,确保在你的项目中引入ui-router。推荐使用国内CDN:

<script src="https://cdn.bootcdn.net/ajax/libs/angular.js/1.7.9/angular.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/angular-ui-router/1.0.29/angular-ui-router.min.js"></script>
2. 定义主模块
angular.module('dashboardApp', ['ui.router'])
3. 配置路由状态

使用$stateProvider配置应用状态,代码组织如下:

angular.module('dashboardApp')
  .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
    
    // 默认路由
    $urlRouterProvider.otherwise('/dashboard/home');
    
    // 定义路由状态
    $stateProvider
      // 抽象状态 - 仪表盘布局
      .state('dashboard', {
        abstract: true,
        url: '/dashboard',
        templateUrl: 'templates/dashboard.html',
        controller: 'DashboardController',
        controllerAs: 'vm'
      })
      
      // 首页
      .state('dashboard.home', {
        url: '/home',
        views: {
          'content': {
            templateUrl: 'templates/dashboard-home.html',
            controller: 'HomeController',
            controllerAs: 'home'
          }
        }
      })
      
      // 统计数据页面
      .state('dashboard.stats', {
        url: '/stats',
        views: {
          'content': {
            templateUrl: 'templates/dashboard-stats.html',
            controller: 'StatsController',
            controllerAs: 'stats',
            resolve: {
              // 预加载统计数据
              statsData: ['StatsService', function(StatsService) {
                return StatsService.getDashboardData();
              }]
            }
          }
        }
      })
      
      // 销售统计子页面
      .state('dashboard.stats.sales', {
        url: '/sales',
        views: {
          'stats-content': {
            templateUrl: 'templates/stats-sales.html',
            controller: 'SalesStatsController',
            controllerAs: 'sales'
          }
        }
      });
  }]);

在上面的代码中,我们使用了抽象状态dashboard作为布局容器,它的模板文件templates/dashboard.html包含了整个应用的框架结构。

4. 创建布局模板

templates/dashboard.html内容如下:

<div class="dashboard-container">
  <!-- 顶部导航栏 -->
  <header class="dashboard-header">
    <h1>企业数据仪表盘</h1>
    <nav class="main-nav">
      <!-- 导航链接 -->
      <a ui-sref-active="active" ui-sref="dashboard.home">首页</a>
      <a ui-sref-active="active" ui-sref="dashboard.stats">统计</a>
      <a ui-sref-active="active" ui-sref="dashboard.settings">设置</a>
    </nav>
  </header>
  
  <div class="dashboard-body">
    <!-- 侧边栏 -->
    <aside class="dashboard-sidebar">
      <ul class="sidebar-menu">
        <li ng-repeat="item in vm.menuItems">
          <a ui-sref="{{item.state}}">{{item.label}}</a>
        </li>
      </ul>
    </aside>
    
    <!-- 主内容区 - 命名视图 -->
    <main class="dashboard-content">
      <ui-view name="content"></ui-view>
    </main>
  </div>
  
  <!-- 页脚 -->
  <footer class="dashboard-footer">
    © 2025 企业数据平台 - 版权所有
  </footer>
</div>

这里的<ui-view name="content"></ui-view>就是我们定义的命名视图,对应状态配置中的views: { 'content': { ... } }部分。

5. 实现控制器

DashboardController负责管理整个仪表盘的共享状态,如侧边栏折叠/展开:

angular.module('dashboardApp')
  .controller('DashboardController', ['$scope', function($scope) {
    const vm = this;
    
    // 侧边栏状态
    vm.sidebarCollapsed = false;
    
    // 切换侧边栏
    vm.toggleSidebar = function() {
      vm.sidebarCollapsed = !vm.sidebarCollapsed;
    };
    
    // 菜单数据
    vm.menuItems = [
      { label: '首页', state: 'dashboard.home', icon: 'home' },
      { label: '统计数据', state: 'dashboard.stats', icon: 'bar-chart' },
      { label: '设置', state: 'dashboard.settings', icon: 'settings' },
      { label: '个人资料', state: 'dashboard.profile', icon: 'user' }
    ];
  }]);
6. 子状态实现 - 统计数据页面

统计数据页面需要展示多个数据卡片,我们使用命名视图实现复杂布局:

// 销售统计子页面
.state('dashboard.stats.sales', {
  url: '/sales',
  views: {
    'stats-content': {
      templateUrl: 'templates/stats-sales.html',
      controller: 'SalesStatsController',
      controllerAs: 'sales',
      resolve: {
        salesData: ['StatsService', function(StatsService) {
          return StatsService.getSalesData();
        }]
      }
    },
    'chart@sales': {
      templateUrl: 'templates/components/sales-chart.html',
      controller: 'SalesChartController',
      controllerAs: 'chart'
    },
    'summary@sales': {
      templateUrl: 'templates/components/sales-summary.html',
      controller: 'SalesSummaryController',
      controllerAs: 'summary'
    }
  }
});

对应的模板文件中使用多个命名视图:

<div class="stats-container">
  <div class="row">
    <div class="col-md-8">
      <ui-view name="chart"></ui-view>
    </div>
    <div class="col-md-4">
      <ui-view name="summary"></ui-view>
    </div>
  </div>
  <div class="row">
    <ui-view name="stats-content"></ui-view>
  </div>
</div>

高级功能实现

1. 使用Resolve预加载数据

src/stateProvider.ts中定义的resolve功能允许我们在控制器实例化之前预加载数据:

.state('dashboard.stats', {
  url: '/stats',
  views: {
    'content': {
      templateUrl: 'templates/dashboard-stats.html',
      controller: 'StatsController',
      controllerAs: 'stats',
      resolve: {
        // 预加载统计数据
        statsData: ['StatsService', function(StatsService) {
          return StatsService.getDashboardData();
        }],
        // 可以有多个resolve
        userPermissions: ['AuthService', function(AuthService) {
          return AuthService.getPermissions();
        }]
      }
    }
  }
});

然后在控制器中直接使用这些预加载的数据:

.controller('StatsController', ['statsData', 'userPermissions', function(statsData, userPermissions) {
  const vm = this;
  vm.stats = statsData;
  vm.permissions = userPermissions;
}]);

2. 状态切换动画

利用AngularJS的动画系统和ui-router的状态切换事件,实现视图切换动画:

/* 添加过渡动画样式 */
.view-container {
  position: relative;
}

.view-frame {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.view-enter, .view-leave {
  transition: all 0.3s ease;
}

.view-enter {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}

.view-enter-active {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.view-leave {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.view-leave-active {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}

在应用中配置动画:

angular.module('dashboardApp')
  .animation('.view-frame', ['$animateCss', function($animateCss) {
    return {
      enter: function(element, done) {
        return $animateCss(element, {
          from: { opacity: 0, transform: 'translate3d(100%, 0, 0)' },
          to: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
          duration: 0.3
        }).start().done(done);
      },
      leave: function(element, done) {
        return $animateCss(element, {
          from: { opacity: 1, transform: 'translate3d(0, 0, 0)' },
          to: { opacity: 0, transform: 'translate3d(-100%, 0, 0)' },
          duration: 0.3
        }).start().done(done);
      }
    };
  }]);

3. 动态参数与状态监听

监听路由参数变化,实现数据的动态更新:

.controller('SalesStatsController', ['$scope', '$stateParams', 'SalesService', function($scope, $stateParams, SalesService) {
  const vm = this;
  
  // 加载销售数据
  function loadSalesData() {
    SalesService.getSalesByPeriod($stateParams.period || 'week')
      .then(function(data) {
        vm.salesData = data;
      });
  }
  
  // 初始加载
  loadSalesData();
  
  // 监听参数变化
  $scope.$on('$stateParamsChange', function(event, params) {
    if (params.period) {
      loadSalesData();
    }
  });
}]);

性能优化建议

  1. 使用抽象状态共享布局:避免重复定义相同的布局结构。

  2. 合理使用resolve:只在必要时使用resolve,避免过多的数据预加载影响页面加载速度。

  3. 状态嵌套优化:不要创建过深的状态嵌套,建议最多3层。

  4. 利用onEnter/onExit:在src/stateProvider.ts中定义的onEnteronExit回调可以处理状态进入和退出时的逻辑,如资源加载和清理。

.state('dashboard.stats', {
  url: '/stats',
  views: { /* ... */ },
  onEnter: ['$modal', function($modal) {
    // 进入状态时打开模态框
    $modal.open({ /* ... */ });
  }],
  onExit: ['$modalStack', function($modalStack) {
    // 退出状态时关闭所有模态框
    $modalStack.dismissAll();
  }]
});
  1. 使用懒加载:对于大型应用,可以考虑使用ocLazyLoad等库实现控制器和模板的懒加载。

总结

通过本文的实例,我们展示了如何使用ui-router构建一个模块化的AngularJS仪表盘应用。关键要点包括:

  • 使用抽象状态构建布局框架
  • 利用命名视图实现复杂布局
  • 通过resolve预加载数据
  • 状态嵌套和参数管理
  • 状态切换动画和过渡效果

这种模块化的路由设计不仅使代码结构更清晰,也提高了代码的复用性和可维护性。通过合理规划状态结构,你可以构建出更复杂、更强大的AngularJS应用。

项目的完整代码结构可以在src/目录下查看,更多高级用法请参考官方文档和源代码注释。

【免费下载链接】ui-router The de-facto solution to flexible routing with nested views in AngularJS 【免费下载链接】ui-router 项目地址: https://gitcode.com/gh_mirrors/ui/ui-router

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值