在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/

示例可重用的web component方式组织angular应用模块的更多相关文章

  1. 使用纯粹的JS构建 Web Component

    原文链接:https://ayushgp.github.io/htm...译者:阿里云 - 也树 Web Component 出现有一阵子了. Google 费了很大力气去推动它更广泛的应用,但是除 ...

  2. Web Component探索

    概述 各种网站往往需要一些相同的模块,比如日历.调色板等等,这种模块就被称为“组件”(component).Web Component就是网页组件式开发的技术规范. 采用组件进行网站开发,有很多优点. ...

  3. Web Component总结

    Web Component 一个Web组件通常由四个部分组成:模板.Shadow DOM.自定义元素与打包,其中Shadow DOM解决了组件在页面中的封装问题 Shadow DOM 有shadow ...

  4. Web Component

    前言 Web Component不是新东西,几年前的技术,但是受限于浏览器兼容性,一直没有大规模应用在项目里,直到现在(2018年年末),除IE仍不支持之外,其它主流浏览器都支持Web Compone ...

  5. WCF服务的Web HTTP方式

    NET 3.5以后,WCF中提供了WebGet的方式,允许通过url的形式进行Web 服务的访问.现将WCF服务设置步骤记录如下: endpoint通讯协议设置成  webHttpBinding en ...

  6. Web Component 文章

    周末无意中了解了Web Component的概念. http://blog.amowu.com/2013/06/web-components.html http://www.v2ex.com/t/69 ...

  7. hadoop笔记之Hive的管理(web界面方式)

    Hive的管理(二) Hive的管理(二) Web界面方式 端口号9999 启动方式:hive --service hwi 通过浏览器来访问:http://<IP地址>:9999/hwi/ ...

  8. js Web存储方式

    JSON是数据交互中最常用的一种数据格式. 由于各种语言的语法都不同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串> 传递之后,可以讲JSON字符串,在解析为JSON对象. ...

  9. Salesforce知识整理(一)之Lightning Web Component Tools

    目录 LWC知识整理(一) 工具 Salesforce CLI Visual Studio Code(VS Code) Developer Hub(Dev Hub) 开启Dev Hub 相关资料 茶余 ...

随机推荐

  1. JavaScript之isNaN()函数讲解

    定义和用法 isNaN() 函数用于检查其参数是否是非数字值. 语法 isNaN(x) 参数 描述 x 必需.要检测的值. 返回值 如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返回的 ...

  2. SQLserver分页查询实例

    Sqlserver数据库分页查询一直是Sqlserver的短板,闲来无事,想出几种方法,假设有表ARTICLE,字段ID.YEAR...(其他省略),数据53210条(客户真实数据,量不大),分页查询 ...

  3. 流程控制语句和增强for循环

    import java.lang.Math; //import java.util.Arrays; public class test{ public static void main(String[ ...

  4. jdom处理的XML Document 和String 之间的相互转化

    package util; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; ...

  5. POJ 2227 The Wedding Juicer (优先级队列+bfs+dfs)

    思路描述来自:http://hi.baidu.com/perfectcai_/item/701f2efa460cedcb0dd1c820也可以参考黑书P89的积水. 题意:Farmer John有一个 ...

  6. .Net知识点总结(一)

    1.文件上传:Jquery.uploadify  它依赖于flash  舍去起上传   功能  改用SWFupload  他是第三方的插件 2.验证码激活的时候,邮箱开始是写死的,但是为了以后更改邮箱 ...

  7. **app后端设计(10)--数据增量更新(省流量)

    在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地. 使用数据的app本地存储,能减少网络的流量,同时极大提高了用户 ...

  8. 多页面打印--web print

    背景:项目中要求做在一个页面中通过选择网址来打印多个页面的内容的功能 原理:通过iframe把各网址的页面内容加载进来,通过iframe.contentWindow拿到iframe的window对象, ...

  9. Java多线程-线程的调度(守护线程)

    本文转自http://www.cnblogs.com/linjiqin/p/3210004.html 感谢作者 守护线程与普通线程写法上基本没啥区别,调用线程对象的方法setDaemon(true), ...

  10. java消息队列

    来个个人通俗的解释吧.消息队列,顾名思义 首先是个队列.队列的操作有入队和出队 也就是你有一个程序在产生内容然后入队(生产者) 另一个程序读取内容,内容出队(消费者) 我想你应该是缺乏一个使用场景. ...