一篇文章看懂angularjs component组件

壹 ❀ 引
贰 ❀ 创建一个简单component
<body ng-controller="myCtrl">
<my-name></my-name>
<you-name></you-name>
</body>
angular.module('myApp',[])
    .controller('myCtrl',function () {})
    .component('myName',{
        template:'<div>我的名字是听风是风</div>'
    })
    .directive('youName',function (){
        return{
            restrict:'AE',
            replace:true,
            template:'<div>你的名字是陌生人</div>'
        }
    })

可以看到directive需要在注册名字后紧接回调函数,并在回调函数中返回一个包含directive配置的对象;而component简单很多,component名后面只用紧跟一个包含配置的对象即可,一个完全的component应该是这样:
angular.module('myApp', [])
    .controller('myCtrl', function () {})
    .component('componentName', {
        template: 'String or Template Function',
        templateUrl:String,
        transclude:Boolean,
        bindings: {},
        controllerAs:String,
        require:String,
        controller: function () {},
    })
属性相比directive简直少了一大半,最直观的就是没了编译函数compile与链接函数link,下面我们一一介绍相关属性。
叁 ❀ 参数详解
1.template /ˈtempleɪt/ 模板
component的template用法与directive保持一致,将你需要渲染的DOM结构以字符串的形式拼接好作为template的值即可,比如在文章开头一个简单的例子就展示了template的用法。
但是有一点与directive不同,directive要求模板文件结构最外层必须使用一个根元素包裹(不管使用template还是templateUrl),但是component并没有这个要求,比如这样:
angular.module('myApp',[])
    .controller('myCtrl',function () {})
    .component('myName',{
        template:'<div>我的名字是听风是风。</div><div>要做一个温柔的人。</div>'
    })

可以看到在template有两个同级的div元素,我并未用一个根元素包裹这两div也能正常显示,但如果是directive这样做就会报错:
    .directive('youName',function (){
        return{
            restrict:'AE',
            replace:true,
            template:'<div>我的名字是听风是风。</div><div>要做一个温柔的人。</div>'
        }
    })

2.templateUrl 模板路径
component的templateUrl用法与directive用法一致,将模板的路径地址作为值赋予给templateUrl即可,比如这样:
angular.module('myApp',[])
    .controller('myCtrl',function () {})
    .component('myName',{
        templateUrl:'../template/myName.html'
    })

需要注意的是加载模板需要服务器,否则会报错,这里给大家推荐一个本地服务器 live-server,用法很简单,大家可以看看。
3.transclude
在使用component时,如果组件中包含了其它DOM结构或者其它组件,你会发现组件解析后,原本的DOM直接消失不见了,看个例子:
<div ng-controller="myCtrl">
<my-name>
<div>要做一个努力的人。</div>
</my-name>
</div>
angular.module('myApp', [])
    .controller('myCtrl', function () {})
    .component('myName', {
        template: '<div >我是听风是风</div>'
    })

在组件my-name中原本还包裹了一个div元素,但是在模板解析后可以看到这个div直接被替换掉了,如果我们想保留这个div就得使用transclude属性,transclude一般与ng-transclude指令一起使用,看个例子:
angular.module('myApp', [])
    .controller('myCtrl', function () {})
    .component('myName', {
        transclude: true,
        template: '<div >我是听风是风</div><div ng-transclude><div>'
    })
HTML结构不变,我们在组件中新增了 transclude:true,并在模板中新增了一个div元素,并为此div元素添加了ng-transclude指令,再看组件解析后就正常了,你会发现你想保留的div元素成了添加了ng-transclude指令元素的子元素。

如果我们要使用组件嵌套,这个属性也是必不可少,关于transclude就说到这。
4.controller
每个组件都拥有自己的controller控制器用于定义组件需要的数据方法等,component的controller值也可以是一个字符串或者一个函数,先说字符串的情况:
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {
        let vm = this;
        this.name = '时间跳跃';
        $scope.age = 26;
    })
    .component('myName', {
        transclude: true,
        template: '<div >我的名字是{{$ctrl.name}},我今年{{age}}了。</div>',
        controller:'myCtrl'
    })

神奇的是我在component中并未定义一个叫myCtrl的构造器,但是component还是解析了数据,这是因为当controller值为字符串时就会从应用中查找与字符串同名的构造函数作为自己的控制器函数,很明显父作用域控制器刚好也叫myCtrl,所以这就直接拿来用了。
这么做有个优点就是,component是默认自带隔离作用域的,也就是说父作用域的数据是无法通过继承传递给子组件,如果你组件自身并没有其它数据方法,通过这种办法倒是可以投机取巧一波。
当然controller我们一般的写法是后面接一个回调函数,像这样:
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {})
    .component('myName', {
        transclude: true,
        template: '<div >我的名字是{{$ctrl.name}},我今年{{age}}了。</div>',
        controller: function ($scope){
            let vm = this;
            this.name = '听风是风';
            $scope.age = 18;
        }
    })

5.controllerAs
我们知道controller与view通信有两种方式,一是通过scope,将数据绑在scope上,视图中通过表达式解析即可渲染,二是通过this绑定,这里的controllerAs就是用来设置控制器的别名,controllerAs默认值为$ctrl,在上面的例子中已经有展示,我们再来看个例子:
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {})
    .component('myName', {
        transclude: true,
        template: '<div >我的名字是{{vm.name}},我今年{{vm.age}}了。</div>',
        controllerAs: 'vm',
        controller: function ($scope) {
            let vm = this;
            this.name = '听风是风';
            this.age = 18;
        }
    })

在上面的例子中,我们将controllerAs的值设置成vm,那么在模板中使用这个值时就是通过vm访问,如果大家对于scope与控制器controller的this有何区别存在疑惑,可以阅读博主这篇文章 angularjs $scope与this的区别,controller as vm有何含义?
6.bindings 父传值给子组件
还记得directive有一个scope属性可以决定directive是否创建隔离作用域,如果scope的值为对象,则表示指令创建隔离作用域,不再继承父作用域中的属性,父作用域想传值就得依赖绑定策略。
而component的bindings就是对应directive的scope:{}的情况,component默认创建隔离作用域,如果想使用父作用域的数据,就得使用bindings结合绑定策略,我们来看个例子:
<div ng-controller="myCtrl">
<my-name user-name="name" say-name="sayName"></my-name>
</div>
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {
        $scope.name = "听风是风";
        $scope.sayName = function () {
            console.log($scope.name);
        };
    })
    .component('myName', {
        transclude: true,
        template: '<div >我的名字是{{vm.userName}}。</div><button ng-click="vm.sayName()">点我</button>',
        controllerAs: 'vm',
        bindings:{
            userName:'<',
            sayName:'<'
        }
    })

在HTML中的组件上,我们以key-value的形式传值我们需要在组件中访问的属性和方法,注意key如果是多个单词建议使用 - 拼接,但在bindings中得改为小驼峰形式,这样我们就可以在模板中直接使用了。
在bindings中我们可以看到需要传值的数据后面跟了一个<符号,这是绑定策略的规则,<表示单项绑定,即数据传递给组件后,父作用域如果修改了数据,子会同步改变,但如果子修改不会修改父,看个例子:
<div ng-controller="myCtrl">
我是父作用域:<input type="text" ng-model="name"><br>
<my-name user-name="name" say-name="sayName"></my-name>
</div>
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {
        $scope.name = "听风是风";
    })
    .component('myName', {
        transclude: true,
        template: '我是组件:<input ng-model="vm.userName">',
        controllerAs: 'vm',
        bindings:{
            userName:'<',
        }
    })

但如果我们将<改为 = 符号表示双向绑定,不管修改父还是子,双方都会同步更新,改成=之后是这样:

但需要注意的一点是,如果传递的数据是一个对象,由于浅拷贝的缘故,不管你用 = 还是<,如果修改了对象的属性,父子都会同步更新。
但是上面的例子是传递过来后直接给模板在使用,如果我想在组件的controller中使用怎么办呢,如果你尝试在控制器中打印传过来的值,你会发现是拿不到的,这里得借用钩子函数,比如onInit,我们来看个例子:
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {
        $scope.name = "听风是风";
    })
    .component('myName', {
        transclude: true,
        template: '我是组件:<input ng-model="vm.userName">',
        controllerAs: 'vm',
        bindings: {
            userName: '=',
        },
        controller: function ($scope) {
            console.log(this.userName);// undefined
            console.log($scope.userName);// undefined
            this.$onInit = function () {
                console.log(this.userName);// 听风是风
                console.log($scope.userName);// undefined
            };
        }
    })

还是一样的传值,我分别在钩子函数内外打印了this.userName与$scope.userName,首先可以确定的是只能在钩子函数内访问到传递的值,那为什么this可以访问而$scope访问不到呢,因为component传值是绑定在控制器上的,所以只能通过this访问。
关于钩子函数我会单独利用一篇博客介绍,这里先挖坑,另外directive传值是绑定在scope上的,所以不需要$onInit你都能直接通过scope访问。
7.require 引用父级组件的控制器
我们知道directive指令的require属性能帮助当前指令引用父级指令中指令中控制器上的所有属性方法,当然component同样提供了这个属性,只是用法上有点区别。
directive的require值是一个字符串(引用单个)或者一个数组(引用多个),而component中require值是一个对象,写法上也有点区别,我们来看一个完整的例子:
<div ng-controller="myCtrl">
<her-name>
<your-name>
<my-name></my-name>
</your-name>
</her-name>
</div>
angular.module('myApp', [])
    .controller('myCtrl', function ($scope) {
        $scope.name = "听风是风";
    })
    .directive('herName', function () {
        return {
            restrict: 'AE',
            replace: true,
            controller: function () {
                this.name = '伊莎贝拉';
            }
        }
    })
    .component('yourName', {
        controllerAs: 'vm',
        controller: function ($scope) {
            this.name = '时间跳跃';
        }
    })
    .component('myName', {
        template: '<div>她的名字是{{vm.she.name}},你的名字是{{vm.you.name}},我的名字是{{vm.myName}}.</div>',
        controllerAs: 'vm',
        require: {
            she: '?^herName',
            you: '?^yourName'
        },
        controller: function () {
            this.$onInit = function () {
                console.log(this.she.name);
                console.log(this.you.name);
            };
            this.myName = '听风是风';
        }
    });

component的require有点bindings的意思,需要在组件内部指定一个变量来保存引用的组件控制器,通过例子我们也可以知道,require不仅可以引入组件还可以引入指令。
在Angular 1.5.6版本之后,如果require对象中key名和require的控制器同名,那么就可以省略控制器名,所以上面的require还可以简写成这样:
require: {
    herName: '^^',
    yourName: '^^'
},
一个 ^ 表示从自己开始找,一直找到到上级组件,而^^表示直接从父级组件开始找,算是一个小技巧。
伍 ❀ 总
那么到这里component用法及属性就介绍完,说到底component就是阉割版的directive,用法上还是大同小异,directive怎么玩到了component还是一样,但从创建组件角度来说,component确实更简单更方便。
如果你要在组件编译阶段或者链接阶段做什么操作,或者说要操作DOM,component就无法满足你的需求,毕竟component未提供编译与链接函数,而且component默认只有使用element创建组件,并不支持属性类名或注释。
聊完了directive指令与component组件,你是否觉得这两兄弟看着相似却又有一些不同,如果你觉得让你有一些糊涂,可以阅读博主这篇文章 angularjs中directive指令与component组件有什么区别?。
那么本文到这里就真是结束了,希望对你有帮助。
一篇文章看懂angularjs component组件的更多相关文章
- angularjs 一篇文章看懂自定义指令directive
		
壹 ❀ 引 在angularjs开发中,指令的使用是无处无在的,我们习惯使用指令来拓展HTML:那么如何理解指令呢,你可以把它理解成在DOM元素上运行的函数,它可以帮助我们拓展DOM元素的功能.比如 ...
 - 一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?
		
壹 ❀ 引 我觉得每一位JavaScript工作者都无法避免与闭包打交道,就算在实际开发中不使用但面试中被问及也是常态了.就我而言对于闭包的理解仅止步于一些概念,看到相关代码我知道这是个闭包,但闭包 ...
 - 一篇文章看懂spark 1.3+各版本特性
		
Spark 1.6.x的新特性Spark-1.6是Spark-2.0之前的最后一个版本.主要是三个大方面的改进:性能提升,新的 Dataset API 和数据科学功能的扩展.这是社区开发非常重要的一个 ...
 - 一篇文章看懂JS执行上下文
		
壹 ❀ 引 我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.lo ...
 - rabbitMQ教程(二)一篇文章看懂rabbitMQ
		
一.rabbitMQ是什么: RabbitMQ,遵循AMQP协议,由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上. 学过websocket的来理解rabbitMQ应该是 ...
 - 一篇文章看懂Java并发和线程安全
		
一.前言 长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章. 二.导读 1.为什 ...
 - rabbitMQ教程(三)一篇文章看懂rabbitMQ
		
一.rabbitMQ是什么: RabbitMQ,遵循AMQP协议,由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上. 学过websocket的来理解rabbitMQ应该是 ...
 - 一篇文章看懂Facebook和新浪微博的智能FEED
		
本文来自网易云社区 作者:孙镍波 众所周知,新浪微博的首页动态流不像微信朋友圈是按照时间顺序排列的,而是按照一种所谓的"智能排序"的方式.这种违背了用户习惯的排序方式一直被用户骂, ...
 - 一篇文章看懂iOS代码块Block
		
block.png iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返 ...
 
随机推荐
- 当placeholder的字体大小跟input大小不一致时,实现placeholder垂直居中
			
如图:搜索和图标不是垂直居中着实难受 最终通过如下代码实现: input::-webkit-input-placeholder { transform: translate(0, 2px); }
 - pipelinedb学习笔记 - 1. Continuous Views (连续视图)
			
Continuous Views 一.Continuous Views 英文直译过来叫连续视图, 在pipelindb中是被定义为专门用来展示 Stream中数据用的.例如:Stream中有一些用户信 ...
 - PHP ftp获取目录内容为空
			
使用PHP的ftp函数获取目录内容,ftp_nlist()和ftp_rawlist()返回都为空. 查了一圈资料找不到答案,然后用Python写了一个,一样的操作就可以获取目录内容. 抓包发现,Pyt ...
 - 跟着文档学习gulp1.1安装入门
			
Step1:检查是否已经安装了node,npm 和 npX是否正确安装 Step2:安装gulp命令行工具(全局安装gulp) npm install --global gulp-cli Step3: ...
 - CodeForces - 556D Case of Fugitive (贪心+排序)
			
Andrewid the Android is a galaxy-famous detective. He is now chasing a criminal hiding on the planet ...
 - 2016/11/10 kettle概述
			
ETL(Extract-Transform-Load,即抽取,转换,加载),数据仓库技术,是用来处理将数据从来源(以前做的项目)经过抽取,转换,加载到达目的端(正在做的项目)的过程.也就是新的项目需要 ...
 - JavaWeb学习——页面跳转方式
			
JavaWeb学习——页面跳转方式 摘要:本文主要学习了请求转发和响应重定向,以及两者之间的区别. 请求转发 相关方法 使用HttpServletRequest对象的 getRequestDispat ...
 - AE单词备忘
			
类的基本特性内 approved 已批准 implemented 已实施 mandatory 强制性的 proposed 偍仪的 validated 已验证
 - 未来已来:云原生 Cloud Native
			
作者:天知,原文链接 前言 自 2013 年容器(虚拟)技术(Docker)成熟后,后端的架构方式进入快速迭代的阶段,出现了很多新兴概念: 微服务 k8s Serverless IaaS:基础设施服务 ...
 - Python连载56-发送带有附件、正文为HTML的邮件
			
一.HTML格式怎么发送右键 1.准备HTML代码作为内容 2.把邮件的subtype设置为html 3.发送 4.举个例子:自己发给自己一个HTML格式的文件 from email.mime.tex ...