如何使指令查找两个参数?

时间:2014-07-14 18:34:42

标签: javascript angularjs

我有两个元素,看起来会更像:

<chart type="type-one" legend="true"></chart>
<chart type="type-two" legend="true"></chart>

每个元素都应该由它自己的指令处理,放在一个单独的文件中。 如何使这些指令查找要处理的元素名称charttype属性?

更新: @vittore,感谢您的答疑!我的问题是我希望有不同类型的图表,这些图表由来自不同文件的指令处理,导致没有一个巨型文件处理chart元素,其中内部函数针对每种类型的图表运行,而是更多模块化文件负责处理每种图表类型。

现在我这样做:

app.directive('chart', function () {
  return {
    restrict: 'E',
    require: 'ngModel',
    scope: {
      'ngModel': '=',
      'showGrouped': '='
    },

    link: function (scope, element, attrs, ctrl) {
      if (attrs.type != 'type-one') {return}
      render()
    }
  }
});

所以我检查了type属性,如果它不是某个值,则返回,否则运行相应的渲染代码。我在每个指令文件中都有这段代码来执行特定的图表类型渲染。我确信这种方法有问题。

请指教!

7 个答案:

答案 0 :(得分:4)

使用范围参数

 angular.module('myModule',[])
        .directive('chart', function() {
           return {
             templateUrl:'chart.html',
             replace:true,
             restrict: 'E',
             scope: {
                type:'=', 
                legend:'='
             }                  
           }
        })

和chart.html

  <div> {{type}} {{legend}} </div>

<强>更新

为了真正需要属性,当提供无效值或根本没有提供某些值时,您可以从指令的linkcompile函数抛出异常。

更新2:

属性范围绑定有3种类型:=&@。你应该适当地使用它们。

如果您只想将字符串传递给指令,可以使用@绑定:

  .directive('chart', function() {
           return {
             templateUrl:'chart.html',
             replace:true,
             restrict: 'E',
             scope: {
                type:'@', 
                legend:'@'
             }                  
           }
        })

这样您的参数将被视为字符串:

 scope.type='true'
 scope.legend='type-one'

或者您可能希望将它们绑定到范围字段:

 <chart type="myModel.type" legend="myModel.legend" />

拥有=范围声明:

 .directive('chart', function() {
           return {
             templateUrl:'chart.html',
             replace:true,
             restrict: 'E',
             scope: {
                type:'=', 
                legend:'='
             }                  
           }
        })

将在指令的scope属性和父scope属性之间创建双向绑定:

  scope.type = $parent.myModel.type
  scope.legend = $parent.myModel.legend

之后,您可以更改父作用域和指令作用域中的两个属性。

最复杂的&绑定,它允许您提供带有父范围参数的方法,以便从指令调用:

  app.directive("chart", function() {
    return {
      scope: {
        details: "&"
      },
      template: '<input type="text" ng-model="value">' +
        '<div class="button" ng-click="details({legend:value})">Show Details</div>'
    }
  })

和标记

  <chart details='showDetails(legend)'></chart> 

有关每种范围绑定的详细信息,请参阅egghead.io的优秀视频:

答案 1 :(得分:1)

我使用以下内容:

  1. 一个常量,用于保存您在整个应用程序中支持的图表类型。
  2. 每种类型图表的命名控制器。
  3. (可选)要了解当前活动图表及其类型的服务。
  4. 注册应用

    var app = angular.module('app', []);
    

    注册常量以控制我们支持的类型

    app.constant('chartTypes', ['typeOne', 'typeTwo']);
    

    指令

    app.directive('chart', ['$controller', 'chartTypes', function ($controller, chartTypes) {
      return {
    
        restrict: 'E',
    
        scope: {
          'showGrouped': '='
        },
    
        controller: function ($scope, $element, $attrs) {
    
          if (chartTypes.indexOf($attrs.type) === -1) {
    
            throw new Error('Chart type ' + $attrs.type + ' is not supported at the moment');
    
          } else {
    
            return $controller($attrs.type + 'Controller', {
              $scope: $scope,
              $attrs: $attrs
    
            });
          }
        },
    
        link: function (scope, element, attrs, controller) {
    
          // Rock'n'roll...
          controller.init();
    
        }
      }
    }]);
    

    注意:我现在从指令中删除了ngModel要求,使其更加清晰 如何建立这样的东西。我不确定如何让ngModel与此解决方案一起玩, 但是我确定你能想出那个......如果没有,我很乐意以后试一试。

    <强>控制器(一个或多个)

    app.controller('typeOneController', ['$scope', '$attrs', function ($scope, $attrs) {
    
      var ctrl = this;
    
      ctrl.init = function () {
        console.log('We are initialised and ready to rock!');
      };
    
      $scope.someFunc = function () {
        console.log($scope, 'is the isolate scope defined in the directive');
      };
    
    }]);
    

    加价:

    <chart type="typeOne" legend="true"></chart>
    
    <chart type="typeTwo" legend="true"></chart>
    
    <chart type="typeThree" legend="true"></chart>
    

    预期结果

    1. 图表typeOne此时应该正常滚动,注销我们实际上是初始化的。
    2. 图表typeTwo应该抛出错误,指出无法找到该名称的控制器(未定义)。
    3. 图表typeThree应该抛出一个错误,指出当前不支持传入的chartType。
    4. 结束时:

      现在,这不是你的传统指令结构 - 但我认为它是一个未被充分利用的结构。 将链接功能作为控制器的好处是,您可以完全分离 指令定义中的$ scope行为。

      这反过来允许我们单独测试指令本身的$ scope行为,而不是 需要在我们的单元测试中实例化指令及其DOM结构。另外补充道 好处是你不必为不同的图表类型设置多个指令,我们只需要一个控制器(或者,链接行为(如果你愿意的话)) 基于传递给指令定义的图表类型。

      这可以进一步构建,包括服务,什么不可以,然后可以在每个图表的基础上(或在指令定义中,将所有图表注入到控制器中)将所有内容注入到控制器中,你有很多灵活性和易于测试的启动。

      带回家的另一个教训是,作为指令控制器的匿名函数 hard 进行测试,与其他地方定义的命名控制器相比,然后注入指令。分离是黄金。

      让我知道这是否适合您,或者如果您需要更深入的示例来设置它。

      我会看看我是否能够获得一整天上传的那种东西。

      修改

      添加了jsFiddle展示行为,虽然有点简化(不是孤立的范围,没有templateUrl): http://jsfiddle.net/ADukg/5416/

      编辑2 :更新了jsFiddle链接,这个链接是一个隔离的范围,因此调用元素上的ng-click定义不会触发隔离的函数。

      编辑3 :添加了一些$ scope函数的示例。

      编辑4 :添加了另一个jsFiddle,展示了隔离范围和templateURL。更符合您的工作。

      http://jsfiddle.net/ADukg/5417/

答案 2 :(得分:0)

您应该实施一个名称为&#39; chart&#39;的指令。内部链接功能,您可以访问指令的所有属性&amp;相应地实现功能。 Accessing attributes from an AngularJS directive

答案 3 :(得分:0)

我的建议是有一个模板对象并存储分离的视图文件并调用view.html取决于我们从attr获取的type

var app = angular.module('myApp',[]);


    app.directive('chart', function () {
        var templates = {
            'type-one': 'typeOne.html',
            'type-two': 'typeTwo.html'
        }
        return {
            restrict: 'E',
            scope: {
                'type':'@'
            },
            templateUrl: function(tElement, tAttrs) {
                 return templates[tAttrs.type];
            },
            link: function (scope, element, attrs, ctrl) {


            }
        }
    });

答案 4 :(得分:0)

此解决方案基于angularjs源码,介绍了如何为<input></input>实现指令 - ngInput

//function to render text input
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
...
}

//function to render number input
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
...
}

//function to render url input
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
...
}

var inputType = {
  'text': textInputType,
  'number': numberInputType,
  'url': urlInputType,
  ...
}


 //input directive
 var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function(scope, element, attr, ctrl) {
      if (ctrl) {
        //logic to use which one to render
        (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
                                                            $browser);
      }
    }
  };
}];

答案 5 :(得分:0)

你可以拥有&#34; chart&#34;作为指令,然后根据类型属性委托其他指令,即&#34; chart1&#34;,&#34; chart2&#34;做类似以下的事情: Add directives from directive in AngularJS

答案 6 :(得分:0)

How can I make these directives look for both element name to be chart and type attribute to be present to be processed?

图表是指令的名称,因此如果您错误地使用 chart2 作为名称而不是图表,则您的指令本身不会被处理,因此无法检查指令名称中的拼写错误

关于检查type属性并假设您希望它是强制性的&amp;你有某些预定义的类型。我遵循的技术是为每个要处理的类型定义一个工厂/服务,所以如果我有typeOne&amp; typeTwo图表,我将创建两个单独的工厂。

app.factory("typeOneChart", function(){
   var renderOne = function(params) {
     // logic for rendering typeOneChart
   };

   return {
      render : renderOne
   }
})

app.factory("typeTwoChart", function(){
   var renderTwo = function(params) {
     // logic for rendering typeTwoChart
   };

   return {
      render : renderTwo
   }
})

还注意到返回的函数名在所有工厂中都是相同的,所以它实现了一个接口。

然后在指令中,只需编写一个开关盒,将卸载部件送到特定的工厂。

app.directive('chart', function (typeOneChart, typeTwoChart) {

  var linkFunction = function(scope, iElement, iAttrs, ngModelCtrl){
     switch(iAttrs.type) {
        case 'one':
           scope.chartFactory = typeOneChart;
           break;
        case 'two':
           scope.chartFactory = typeTwoChart;
           break;
        default:
           //toastr.error("invalid chart type : " + iAttrs.type);
           throw exception("invalid chart type : " + iAttrs.type);
     }

     scope.chartFactory.render();

  }

  return {
    restrict: 'E',
    require: 'ngModel',
    scope: { 'ngModel': '=', 'showGrouped': '=' },
    link: linkFunction
  }
});

这种方法可以使您的指令代码保持最小,加上如果某人指定了无效的图表类型,它只会抛出异常,或者在屏幕上显示和显示错误,您可以立即查看并修复。

现在,特定类型的所有代码都位于其工厂中......因此控制器无法识别每种图表类型的逻辑。