• 示例可重用的web component方式组织angular应用模块


    在online web应用中,经常有这样的需求,能够让用户通过浏览器来输入代码,同时能够根据不同的代码来做语法高亮。大家已知有很多相应的javascript库来实现语法高亮的功能,比如codemirror就是一个不错的选择。而我们使用angular开发web应用,那么就希望能够使用directive来实现这个功能,以便能够很好的重用。https://www.polymer-project.org/ 可以得到更多可重用web组件的信息

    下面是如何使用我们的  <code-editor>  的:

    <html ng-app="demo">
      <head>
        <title>Posts</title>
      </head>
      <body>
        <code-editor syntax="javascript" ng-model="content.code">
          var sum = function (a, b) {
            return a + b;
          }
        </code-editor>
        <textarea ng-model="content.code" cols="100" rows="50"></textarea>
      </body>
    </html>

    这个directive的目标是:像 <pre> 元素一样接受接受输入代码文本,同时能够和 <ngModel> 一样将输入文本作为model放到变量中去。该directive同时也需要将代码在codemirror代码编辑器中展示。

    首先我们来创建基本的文件夹结构。我们创建一个components/codeEditor目录作为我们整个code editor组件的工作目录。在这个目录中,我们将该组件所用到的javascript库(实际就是codemirror库)以及css文件放到vendor目录中。创建一个新的codeEditor.js作为directive代码,一个code-editor.html模板也放在component根目录中。

    使用这种方式来组织代码实际上就是在遵循web component开发的模式了。现在在code-editor.html模板文件的div元素上初始化codeMirror作为开始。

    code-editor.html:

    <div class="code-editor"></div>

    codeEditor.js:

    angular.module("demo").directive("codeEditor", function(){
      return {
        restrict: "E",
        replace: true,
        scope: {
          syntax: "@",
          theme: "@"
        },
        templateUrl: "components/codeEditor/code-editor.html",
        link: function(scope, element, attrs) {
          var editor = CodeMirror(element[0], {
            mode: scope.syntax || "javascript",
            theme: scope.theme || "vibrant-ink",
            lineNumbers: true
          });
        }
      };
    });

    在上述js代码中,调用codeMirror函数使得div元素具有codeMirror的代码高亮功能,同时lineNumber显示出来。这时已经具有了代码输入和高亮的功能:

    这时代码编辑器已经工作了,但是你发现我们在使用code-editor directive的html文件中的代码却并没有被渲染。这是因为,默认情况下,angular将丢弃code-editor元素的内容,替而代之为该directive的template:code-editor.html的内容。为了让angular能将包含在原始code-editor元素内的内容保存并使用起来,我们需要设置code-editor元素的transclude属性为true.

    另外一点是:我们不准备使用 ng-transclude directive来插入内容。反而,我们将执行一个手动的transclude,以便允许内容直接传递到editor。下面是相关代码:

    codeEditor.js:

    angular.module("demo").directive("codeEditor", function(TextUtils){
      return {
        restrict: "E",
        replace: true,
        transclude: true,
        scope: {
          syntax: "@",
          theme: "@"
        },
        templateUrl: "components/codeEditor/code-editor.html",
        link: function(scope, element, attrs, ctrl, transclude) {
          var editor = CodeMirror(element[0], {
            mode: scope.syntax || "javascript",
            theme: scope.theme || "vibrant-ink",
            lineNumbers: true
          });
          
          transclude(function(clonedEl){
            var initialText = TextUtils.normalizeWhitespace(clonedEl.text());
            editor.setValue(initialText);
          });
        }
      };
    });

    上述代码中,当transclude属性被设置为true,我们在link函数中就有了第5个参数--transclude函数。该函数有几种不同的用法,在我们的例子中,我们将传入一个callback函数作为第一个参数。这个callback被调用时将被传入一个原始transcluded content作为参数,这样我们就可以访问到那块内容并且被传递给editor去。TextUtils是一个简单的服务,半酣一个normlizeWhitespace函数,该函数可以清除leading whitespace.

    最后一件事情是我们需要让ngModel可以和editor工作起来,这样我们就可以将他和input,textarea等绑定起来。第一步,我们需要require ngModel属性。

    angular.module("demo").directive("codeEditor", function(TextUtils){
      return {
        restrict: "E",
        replace: true,
        require: "?ngModel",
        transclude: true,
        scope: {
          syntax: "@",
          theme: "@"
        },
        templateUrl: "components/codeEditor/code-editor.html",
        link: function(scope, element, attrs, ngModelCtrl, transclude) { ... }
      };
    });
      
    // ....

    通过设置require属性,我们告知这个directive从ngModel中包含controller的引用到link函数中。通过增加一个?在directive名称前,我们意味着这是一个选项,以便阻止angular在ngModel并不存在的情况下抛出异常。既然我们在directive的link函数中已经可以访问ngModel的controller了,我们需要了解几个用于同步数据到model的几个函数:

    • ngModelCtrl.$setViewValue(someValue) 

    $setViewValue函数用于在model上赋值。当editor的内容变化时,我们需要调用该函数。

    • ngModelCtrl.$render

    $render属性需要被赋一个函数值。这个函数是一个当model value变化时调用的callback。注意model的value并不会传递到这个函数中去。一旦model的value发生变更时,我们需要设置editor的内容为model的value

    • ngModelCtrl.$viewValue

    $viewValue属性包含model的当前值。每当$render回调被调用时我们需要使用它设置editor的value。

    看看下面的代码:

    angular.module("demo").directive("codeEditor", function($timeout, TextUtils){
      return {
        restrict: "E",
        replace: true,
        require: "?ngModel",
        transclude: true,
        scope: {
          syntax: "@",
          theme: "@"
        },
        templateUrl: "components/codeEditor/code-editor.html",
        link: function(scope, element, attrs, ngModelCtrl, transclude){
          var editor = CodeMirror(element[0], {
            mode: scope.syntax || "javascript",
            theme: scope.theme || "default",
            lineNumbers: true
          });
    
          if(ngModelCtrl) {
            $timeout(function(){
              ngModelCtrl.$render = function() {
                editor.setValue(ngModelCtrl.$viewValue);
              }
            })
          }
    
          transclude(function(clonedEl){
            var initialText = TextUtils.normalizeWhitespace(clonedEl.text());
            editor.setValue(initialText);
    
            if(ngModelCtrl){
              $timeout(function(){
                if(initialText && !ngModelCtrl.$viewValue){
                  ngModelCtrl.$setViewValue(initialText);
                }
    
                editor.on('change', function(){
                  ngModelCtrl.$setViewValue(editor.getValue());
                });
              });
            }
          });
    
          scope.$on('$destroy', function(){
            editor.off('change');
          });
        }
      }
    });

      当model变化时,既然我们使得require ngModel为可选而非必选,我们需要确保ngModel controller是否确实存在。在$render函数中,我们将model的value使用$viewValue属性赋值到editor的contents中去。这将避免一个Undefined value被设置到editor中去。

    每当editor变更时,我们设置model的value。同时我们使用一个$timeout来调用它是因为为了等待model初始化。

    最后我们删除和editor关联的用于更新model的event handler。当你使用非angular事件时,他们需要在$destroy回调中被清除。这将确保directive完全清除避免memory leak。

    https://www.codeschool.com/blog/2015/03/06/digging-advanced-angularjs-directives/

  • 相关阅读:
    phpcms V9 联动菜单的调用
    【转】雷军:扁平化管理 不打卡不设KPI
    【转】 Json转换利器Gson之实例五-注册TypeAdapter及处理Enum类型
    【转】Json转换利器Gson之实例四-实际开发中的特殊需求处理
    【转】 Json转换利器Gson之实例三-Map处理
    【转】 Json转换利器Gson之实例二-Gson注解和GsonBuilder
    【转】Json转换利器Gson之实例一-简单对象转化和带泛型的List转化
    【转】工作分解结构在软件开发中的应用
    【转】 IT项目管理的三个条件、五个步骤
    【转】 项目组内个角色的职责说明
  • 原文地址:https://www.cnblogs.com/kidsitcn/p/4509497.html
Copyright © 2020-2023  润新知