【问题标题】:DRY up logic in view as well as $routing在视图中干燥逻辑以及 $routing
【发布时间】:2015-08-20 17:55:13
【问题描述】:

我仍在学习 Angular 并已开始创建一个简单的网站。当 url 在该页面上时,我认为在菜单项上有一个活动类的路由和一些逻辑。我开始看到的问题是,如果我将 url 从 / 更改为 /home,我必须在路由和视图中更新它,以及查看它是否应该处于活动状态的逻辑。

我想知道有没有更好的方法让它变干?

我的 app.js 文件如下所示:

    angular.module('simpleApp', ['ngRoute'])
  .config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home/index.html'
      })
      .when('/about', {
        templateUrl: 'views/about/index.html',
        controller: 'AboutController',
        controllerAs: 'aboutCtrl'
      })
      .when('/services', {
        templateUrl: 'views/services/index.html',
        controller: 'ServicesController',
        controllerAs: 'servicesCtrl'
      })
      .when('/contact', {
        templateUrl: 'views/contact/index.html',
        controller: 'ContactController',
        controllerAs: 'contactCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  }]);

我的 index.html 的部分视图如下所示:

<nav class="navbar navbar-inverse" ng-controller="NavController as navCtrl">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="/#/">SimpleApp</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li ng-class="{ 'active': navCtrl.isActive('/')}">
                  <a href="/#/">Home</a>
                </li>
                <li ng-class="{ 'active': navCtrl.isActive('/about')}">
                  <a href="/#/about">About</a>
                </li>
                <li ng-class="{ 'active': navCtrl.isActive('/services')}">
                  <a href="/#/services">Services</a>
                </li>
                <li ng-class="{ 'active': navCtrl.isActive('/contact')}">
                  <a href="/#/contact">Contact Us</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>

如你所见,我在导航控制器上调用了 isActive 方法,但我必须始终确保我传入的 url 是正确的。

这是我的导航控制器:

    angular.module('simpleApp')
  .controller('NavController', function($scope, $location) {
    this.isActive = function (url) {
      return url === $location.path();
    };
  });

【问题讨论】:

  • 我不认为它是重复的,因为这个问题已经回答了我是如何编写代码的。我的问题是如何让它变干。答案之一就是使用自定义指令,因此请看一下。所以谢谢你让我注意到这一点,因为我不知道在这里谷歌/搜索什么以确保我没有复制任何东西
  • 我指的是公认的答案,这是最好的方法。不需要额外的指令或方法。您只需像 .when('/', { templateUrl: 'views/home/index.html', activetab: 'home' }) 那样向您的路线添加一个自定义属性,然后在您的 html 中您可以这样做 &lt;li ng-class="{active: $route.current.activetab == 'home'}"&gt;&lt;/li&gt;
  • 我目前正在这样做,但它必须在两个地方更改它,而我试图只在一个地方更改它
  • 啊,我明白了,那么指令可能是你最好的选择。实际上,我更喜欢 ui-router 模块而不是 ngRoute 模块来进行路由。它的功能要强大得多,并且已经有一个内置指令来处理许多其他事情中的活动选项卡。如果您还没有,它绝对值得一看。

标签: javascript angularjs


【解决方案1】:

如果您不想重复自己,为什么不直接使用ng-repeat

<ul class="nav navbar-nav">
  <li ng-repeat="item in navbarLinks" ng-class="{ 'active': navCtrl.isActive(item.link)}">
     <a href="/#{{item.link}}">{{item.text}}</a>
  </li>
</ul>

我的意思是,不必重复自己就是它存在的原因。所以你不要重复自己。 ng-repeat 实际上对于那些不想重复自己的情况非常有用。如果你想避免重复自己,我建议你使用它。 “它”是指ng-repeat。避免重复。

【讨论】:

  • 这实际上是一个不错的主意。我确实通过使用 ui 路由器修复了它,但这也是一个很好的方法,很高兴知道。
  • @Jan 嗨,我有一个疑问。您的这段代码正在为所有项目调用 navCtrl.isActive 函数。所以假设我在 navbarLinks 中有 50 个项目,那么 isActive 函数被称为 50 个项目。这优雅吗?
  • @overlord 好吧,这取决于。另一种方法是在每个导航栏项目上设置一个属性?然后,该属性将需要在路由可能更改的任何地方进行更新。会不会很优雅?这取决于代码的其余部分。
【解决方案2】:

查看 ng-class 指令。

https://docs.angularjs.org/api/ng/directive/ngClass

基本上,您的菜单项上有一个 ng-class 引用,它使用表达式部分将菜单项与活动项进行比较,然后当该表达式的计算结果为 true 时,该类将应用于该项。

【讨论】:

  • 是的,不知道我之前是怎么错过的。我将在下面添加一个更好的答案。
  • 好的,不用担心。你的第二个答案被截断了吗?
  • 是的。我在这里是个菜鸟。我没有意识到我需要一个标签。
【解决方案3】:

这将是自定义指令的好地方。该指令可以像上面一样呈现 html,但文档中的实际 html 看起来像 &lt;myNav url='about'&gt;

【讨论】:

  • 我已经编辑了我的问题以使用指令,但因为我使用 / 作为我的主页,它不会使主页链接处于活动状态。有什么想法吗?
  • 那里的工作令人印象深刻。我相信你只需要在代码中检查空白路径,找到它后,在主页链接上设置类。您可能会在指令中添加一个可选参数,该参数仅在主页链接上设置,以表明当 URL 只是 / 时,它是获取活动类的那个。
  • 谢谢,我最后确实想通了,但是转到了 ui 路由器,它为我完成了这一切。
  • 你能发布当前的 HTML,只是为了完整吗?我实际上认为我需要在几天内做类似的事情。
  • 你确定。我将使用完整的 html 和指令更新我的答案
【解决方案4】:

我终于从可能的重复问题中弄清楚了我需要做什么,但稍微改变了它以使用“/”网址。

这是我的 index.html

<!DOCTYPE html>
<html>
<head>
  <title>Simple Angular App</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="css/styles.css">
</head>
  <body ng-app="simpleApp">
    <header>
      <div class="container">
        <nav class="navbar navbar-inverse">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="/#/">SimpleApp</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li>
                  <a href="#/" detect-active-tab="1">Home</a>
                </li>
                <li>
                  <a href="#/about" detect-active-tab="1">About</a>
                </li>
                <li>
                  <a href="#/services" detect-active-tab="1">Services</a>
                </li>
                <li>
                  <a href="#/contact" detect-active-tab="1">Contact Us</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
      </div>
    </header>
    <main>
      <div class="container">
        <div class="row">
          <div ng-view></div>
        </div>
      </div>
    </main>
    <footer>
      <div class="container">
        <div class="row">
          <div class="col-xs-12">
            <p class="footer-text">First attempt at an AngularJs site</p>
          </div>
        </div>
      </div>
    </div>
  </footer>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
    <script src="https://code.angularjs.org/1.4.4/angular-route.min.js"></script>

    <script src="js/app.js"></script>

    <script src="js/directives/shared/navdirective.js"></script>

    <script src="js/controllers/contact/ContactController.js"></script>
    <script src="js/controllers/about/AboutController.js"></script>
    <script src="js/controllers/services/ServicesController.js"></script>
  </body>
</html>

这是我的 app.js:

angular.module('simpleApp', ['ngRoute'])
  .config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home/index.html'
      })
      .when('/about', {
        templateUrl: 'views/about/index.html',
        controller: 'AboutController',
        controllerAs: 'aboutCtrl'
      })
      .when('/services', {
        templateUrl: 'views/services/index.html',
        controller: 'ServicesController',
        controllerAs: 'servicesCtrl'
      })
      .when('/contact', {
        templateUrl: 'views/contact/index.html',
        controller: 'ContactController',
        controllerAs: 'contactCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });

      // if(window.history && window.history.pushState) {
      //   $locationProvider.html5Mode({
      //     enabled: true,
      //     requireBase: false
      //   });
      // }
  }]);

这是我的导航指令文件:

angular.module('simpleApp')
  .directive('detectActiveTab', function ($location) {
    return {
      restrict: 'A',
      link: function postLink(scope, element, attrs) {
        scope.$on("$routeChangeSuccess", function (event, current, previous) {
            // This var grabs the tab-level off the attribute, or defaults to 1
            var pathLevel = attrs.detectActiveTab || 1;

            if($location.path() === '/') {
              var pathToCheck = $location.path() ||
                  "current $location.path doesn't reach this level";
              var tabLink = attrs.href.split('#')[pathLevel] ||
                  "href doesn't include this level";
            } else {
              var pathToCheck = $location.path().split('/')[pathLevel] ||
                  "current $location.path doesn't reach this level";
                  // This var finds grabs the same level of the href attribute
              var tabLink = attrs.href.split('/')[pathLevel] ||
                  "href doesn't include this level";
            }

            if (pathToCheck === tabLink) {
              element.parent('li').addClass('active');
            }
            else {
              element.parent('li').removeClass('active');
            }
        });
      }
    };
  });

为了使用它,我将 detect-active-tab="1" 添加到我的 a 标记中,然后将该指令包含到我的 index.html 中

我还查看了 ui 路由器,它使事情变得更加简单,因为路由的设置与 ngRoute 的设置方式非常相似,但您只需将 ui-sref="/services" 添加到我的链接并将 ui-sref-active="active" 添加到我的 li 的,它对我来说是活跃的状态,这非常好。

【讨论】:

  • 我没有看到你的代码有任何改进。与几个字符立即可读的代码不同,你有一个毛茸茸的怪物,它的功能太复杂了,因为它的简单目的太复杂了。您只是成功地降低了代码的可读性和可维护性。如果您打算采取任何措施减少重复,请将您的整个导航栏更改为基于对象数组的ng-repeat
  • 这就是我第一次问的问题的答案。我把它改成了 ui route 就像我在下面说的那样,这意味着我摆脱了指令并且它更容易阅读。
  • 我不确定添加 10 倍于你所做的代码和更复杂的代码是否应该是任何事情的答案......
  • 给这只猫剥皮的方法有很多。我提出的解决方案解决了我的问题,但给了我一个复杂的问题,这就是我选择 ui 路由器的原因。
  • 剥猫皮的方法有很多。您可以使用正则表达式或 Perl 或安装 DLL 或... 找到给猫剥皮的最佳方法是编程的意义所在。如果您添加 10 倍以上的代码,增加复杂性,损害可读性和可维护性,并且正如您所说,它引入了其他问题,那么这是给猫剥皮的坏方法。因此,即使它可能是您问题的替代答案,也不应被视为您问题的答案。
【解决方案5】:

虽然我的第一个答案确实解决了我原来的问题。它给我带来了其他问题,因为它非常复杂且难以维护。我决定切换到 UI 路由器,这让事情变得更容易。

我的 app.js 文件现在看起来像这样:

angular.module('simpleApp', ['ui.router'])
  .config(function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');

    $stateProvider
      .state('/', {
        url: '/',
        templateUrl: 'views/home/index.html'
      })
      .state('/about', {
        url: '/about',
        templateUrl: 'views/about/index.html',
        controller: 'AboutController',
        controllerAs: 'aboutCtrl'
      })
      .state('/services', {
        url: '/services',
        templateUrl: 'views/services/index.html',
        controller: 'ServicesController',
        controllerAs: 'servicesCtrl'
      })
      .state('/contact', {
        url: '/contact',
        templateUrl: 'views/contact/index.html',
        controller: 'ContactController',
        controllerAs: 'contactCtrl'
      });
  });

我的 html 看起来像这样:

    <!DOCTYPE html>
<html>
<head>
  <title>Simple Angular App</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="css/styles.css">
</head>
  <body ng-app="simpleApp">
    <header>
      <div class="container">
        <nav class="navbar navbar-inverse">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="/#/">SimpleApp</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li ui-sref-active="active">
                  <a ui-sref="/">Home</a>
                </li>
                <li ui-sref-active="active">
                  <a ui-sref="/about">About</a>
                </li>
                <li ui-sref-active="active">
                  <a ui-sref="/services">Services</a>
                </li>
                <li ui-sref-active="active">
                  <a ui-sref="/contact">Contact Us</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
      </div>
    </header>
    <main>
      <div class="container">
        <div class="row">
          <div ui-view></div>
        </div>
      </div>
    </main>
    <footer>
      <div class="container">
        <div class="row">
          <div class="col-xs-12">
            <p class="footer-text">First attempt at an AngularJs site</p>
          </div>
        </div>
      </div>
    </div>
  </footer>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
    <script src="js/lib/angular-ui-router.min.js"></script>

    <script src="js/app.js"></script>

    <script src="js/controllers/contact/ContactController.js"></script>
    <script src="js/controllers/about/AboutController.js"></script>
    <script src="js/controllers/services/ServicesController.js"></script>
  </body>
</html>

我所要做的就是在我的 index.html 中添加对 UI 路由器的引用,并根据 UI 路由器的文档更改配置。然后将ui-srefui-sref-active="active" 添加到li 以使其处于活动状态,然后UI 路由器的魔力完成剩下的工作。

【讨论】:

    猜你喜欢
    • 2010-11-26
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    相关资源
    最近更新 更多