AngularJSWeb前端发展简史

Web前端发展简史

有人说“前端开发”是IT界最易让误解的职位,这不是小道消息。如果您还觉得前者只是从画那里将到切图,
JS和CSS一番乱烧,难打的效力就是失去网上信手拈来,CtrlC + Ctrl
V的话,那就算刚刚饱受了这卖误解的下怀。经过十几年之向上,web前端早已脱离了本边缘化的象,扮演了移动互联网支付链条被极要紧之角色,是行使或产品能否撼动用户之踹门砖。那么什么是前端开发,其又饱含了如何内容?

 

前端开发的概念

自狭义的概念来拘禁,“前端开发”是依靠围绕HTML、JavaScript、CSS这样平等拟系统之开发技术,它的运作宿主是浏览器。从广义的概念来拘禁,其当包括:


专门为手执终端设计的接近WML这样的类HTML语言,以及近似WMLScript的类JavaScript语言。

l  VML和SVG等因XML的叙说图形的语言。

l  从属于XML体系的XML,XPath,DTD等技术。

l  用于支持后端平的ASP,JSP,ASP.net,PHP,Nodejs等语言还是技术。

l  于第三着先后打包的同栽恍若浏览器的宿主环境,比如Adobe
AIR和利用HyBird方式的组成部分开发技术,如PhoneGap。

l  Adobe Flash,Flex,Microsoft Silverlight,Java
Applet,JavaFx等RIA开发技术。

正文主要由“web前端”,也就是狭义前端的角度出发,以人类科技进步划时代的办法,将前端开发划分为几只重大之时期,带领大家理解一下前端这十几年来之进步历程。

 

石器时代

极初期的Web界面基本还是以互联网及采用,人们浏览某些内容,填写几单表单并且付诸。当时底界面以浏览为主,基本还是HTML代码,我们来拘禁一个最为简单易行的HTML文件:

<html>

<head>

<title>测试一</title>

</head>

<body>

<h1>主标题</h1>

<p>段落内容</p>

</body>

</html>

为实施有动作要开展得之事体处理,有时候会越过插有JavaScript,如作为客户端校验这样的基础功用。代码的团体比较简单,而且CSS的应用也是比少的。譬如:下面是文档将含有一段JavaScript代码,用于拼接两独输入框中的字符串,并且弹来窗口亮。

<html>

<head>

<title>测试二</title>

</head>

<body>

<inputid="firstNameInput"type="text"/>

<inputid="lastNameInput"type="text"/>

<inputtype="button"onclick="greet()"/>

<scriptlanguage="JavaScript">

function greet(){

var firstName = document.getElementById("firstNameInput").value;

var lastName = document.getElementById("lastNameInput").value;

            alert("Hello, "+ firstName +"."+ lastName);

}

</script>

</body>

</html>

出于静态界面不克促成保存数据等职能,出现了过多服务端技术,早期的产生CGI(Common
Gateway
Interface,多数为此C语言或者Perl实现的),ASP(使用VBScript或者JScript),JSP(使用Java),PHP等等,Python和Ruby等语言为每每叫用来这仿佛用途。

出矣即类技术,在HTML中即好以表单的post功能交由数据了,比如:

<formmethod="post"action="username.asp">

<p>First Name: <inputtype="text"name="firstName"/></p>

<p>Last Name: <inputtype="text"name="lastName"/></p>

<inputtype="submit"value="Submit"/>

</form>

每当是阶段,由于客户端以及劳动端的职责不发明确的剪切,比如生成一个字符串,可以由前端的JavaScript做,也可以由劳动端语言做。所以便以一个界面里,会时有发生点儿栽语言混杂在联合,用<%和%>标记的部分会在服务端执行,输出结果,甚至经常来管数据库连接的代码和页面代码混杂在齐的动静,给保安带来了挺要命的问题。

<html>

<body>

<p>Hello world!</p>

<p>

<%response.write("Hello world from server!")%>

</p>

</body>

</html>

 

青铜时代

青铜时代的出众标志是起了组件化的萌,着眼点主要以文书的细分上。后端组件化比较广泛的做法是,把有一样近乎后端功能独立做成片段,然后另外急需之地方来include进来,典型的起:ASP里面数据库连接的地方,把数据源连接的一些写成conn.asp,然后另外每个需要操作数据库的asp文件包含它。浏览器端则一般对JavaScript脚论文件,把某某一样近乎的Javascript代码写到独的js文件被,界面根据需要,引用不同之js文件;针对界面组件,则一般以frameset和iframe这点儿只标签。某平等不胜块有单独功能的界面写及一个HTML文件,然后于主界面里面把它当一个frame来载入,一般的B/S系统并菜单的道还是如此的。是无是认为挺熟悉?对的,现在多公司之里系统正是这个时期之产物。

另外,还应运而生了一部分基于特定浏览器的客户端组件技术,比如IE浏览器的HTC(HTML
Component)。这种技能最初是为对已有的常用元素附加行为之,后来微场合也就此其来实现控件。微软ASP.NET的片版里,使用这种技能提供了树形列表,日历,选项卡等力量。HTC的长处是同意用户自行扩展HTML标签,可以当温馨的命名空间里定义元素,然后,使用HTML,JavaScript和CSS来兑现它的布局、行为以及观感。这种技能以凡微软的私有技术,所以慢慢变得无那么盛行。Firefox浏览器布其后尘,也推出过同样种植让XUL的技能,也一如既往没有流行起来。

 

铁器时代

此时之彗星是Ajax的出现以及JS基础框架的起来。

AJAX

AJAX其实是同样密密麻麻已经出技术之结缘,早于这名词出现之前,这些技巧的施用就既比较泛了,GMail因为恰当地应用了这些技能,获得了十分好之用户体验。由于Ajax的出现,规模再可怜,效果又好之Web程序日趋出现,在这些程序中,JavaScript代码的多寡迅猛增多。出于代码组织的需要,“JavaScript框架”这个定义逐渐形成,当时底主流是Prototype和Mootools,两者各有千秋,提供了各自方式的面向对象组织思路。

JavaScript基础框架

Prototype框架主要是为JavaScript代码提供了扳平栽集体措施,对一些原生的JavaScript类型提供了部分恢宏,比如数组、字符串,又格外提供了有些实用的数据结构,如:枚举,Hash等,除此之外,还对dom操作,事件,表单和Ajax做了片打包。

Mootools框架的思绪以及Prototype很类似,它对JavaScript类型扩展的艺术别出心裁,所以在马上好像框架中,经常于誉为“最优雅的”对象扩大体系。

起当下片只框架的所提供的力量来拘禁,它们的稳定是基本库,在动用的当儿一般需相当局部外边之库来成功。

靠天未出,谁和争锋?除上述两者以外,还有YUI,jQuery等,JavaScript基础框架在这个时算是得及是繁荣,但是时间曾证明,真正的王者是jQuery。

jQuery以及外的底子框架都有所不同,它相于简化DOM相关的代码。例如:

l  DOM的选取

jQuery提供了同等多样选择器用于选取界面元素,在任何组成部分框架中吗发近似作用,但一般没它简洁而强劲。

$("*")//选取所有元素

$("#lastname")//选取id为lastname的元素

$(".intro")//选取所有class="intro"的元素

$("p")//选取所有&lt;p&gt;元素

$(".intro.demo")//选取所有 class="intro"且class="demo"的元素

 

l  链式表达式

以jQuery中,可以使用链式表达式来连接操作DOM,如果非以链式表达式,可能要如此写:

var neat = $("p.neat");

neat.addClass("ohmy");

neat.show("slow");

然而有矣链式表达式,一行代码就好搞定:

$("p.neat").addClass("ohmy").show("slow");

除去,jQuery还提供了一些动画片方面的特效代码,也发恢宏之以外库,比如jQuery
UI这样的控件库,jQuery mobile这样的走开发库等。

 

农业时代

这时之标志性事件是模块加载规范(AMD以及CMD)的出现。

铁器时代出现的根基框架提供了代码的集团能力,但是不许提供代码的动态加载能力。动态加载JavaScript为什么要呢?因为随着Ajax的普及,jQuery等辅助库的出现,Web上可以举行充分复杂的作用,因此,单页面应用程序(SPA,Single
Page Application)也慢慢多矣四起。

单个的界面想如果举行多职能,需要写的代码是碰头比较多的,但是,并非有的效果都得以界面加载的时光即便满引入,如果会以用之上才加载那些代码,就管加载的下压力分担了,在是背景下,出现了部分用以动态加载JavaScript的框架,也出现了有的概念这类似可让动态加载代码的科班。

AMD

每当这些框架里,知名度最高的是RequireJS,遵循了AMD(Asynchronous Module
Definition)的正规。比如下面这段,定义了一个动态的匿名模块,它依赖math模块:

define(["math"],function(math){

return{

        addTen :function(x){

return math.add(x,10);

}

};

});

倘上面的代码存放于adder.js中,当得运用是模块的时刻,通过如下代码来引入adder:

<scriptsrc="require.js"></script>

<script>

    require(["adder"],function(adder){

//使用这个adder

});

</script>

RequireJS除了提供异步加载方式,也堪运用并方式加载模块代码。AMD规范除了采取以前者浏览器环境遭受,也可以运行为NodeJS等服务端环境,但是NodeJS内置的模块机制是根据CMD规范定义的。

 

CMD

值得一提的是,在浏览器端,除了RequireJS以外,国内的牛人淘宝玉伯开发了SeaJS异步模块加载器,其按照CMD规范,目前曾有超常300寒大型web应用或站点采用,SeaJS同样略易学:

// 所有模块都通过 define 来定义

define(function(require, exports, module){

// 通过 require 引入依赖

var $ =require(‘jquery’);

var Spinning =require(‘./spinning’);

 

// 通过 exports 对外提供接口

  exports.doSomething =…

 

// 或者通过 module.exports 提供整个接口

  module.exports =…

});

 

工业时代

“这是一个极致好的一时,也是一个顶老之一代。”前端自动化和MV*框架真正给前者迎来了春天,但是是时框架插件众多、体系复杂,让前者新手无所适从。在这时代,Web端功能逐步复杂,人们不得不开始考虑这样局部问题:

l  如何更好地模块化开发

l  业务数据如何组织

l  界面和业务数据中通过何种措施开展互动

每当这种背景下,前端MVC、MVP、MVVM框架而雨后春笋,我们聊把这些框架还统称为MV*框架。这些框架的起,正是以化解上述这些问题,具体的贯彻思路各有不同,主流的有Backbone,AngularJS,Ember三大剑客,本文主要选用Backbone和AngularJS来讲述以下场景。

数据模型

在这些MV*框架里,定义数据模型的艺术跟往常多少差异,主要在于数量的get和set更加有含义了,比如说,可以把某某实体的get和set绑定到RESTful的服务达,这样,对有实体的读写可以创新到数据库中。另外一个特色是,它们一般还提供一个轩然大波,用于监控数据的变,这个机制使得数据绑定成为可能。

当局部框架中,数据模型需要在原生的JavaScript类型上召开同交汇封装,比如Backbone的计是如此:

varTodo=Backbone.Model.extend({

// Default attributes for the todo item.

    defaults :function(){

return{

            title :"empty todo…",

            order :Todos.nextOrder(),

done:false

};

},

 

// Ensure that each todo created has `title`.

    initialize :function(){

if(!this.get("title")){

this.set({

"title":this.defaults().title

});

}

},

 

// Toggle the ‘done’ state of this todo item.

    toggle :function(){

this.save({

done:!this.get("done")

});

}

});

上述例子中,defaults方法用于供模型的默认值,initialize方法用于做一些初始化工作,这半个都是预约的法子,toggle是从定义的,用于保存todo的当选状态。

而外对象,Backbone也支撑集合类型,集合类型在概念的时候如果经过model属性指定其中的要素类型。

// The collection of todos is backed by *localStorage* instead of a remote server.

varTodoList=Backbone.Collection.extend({

// Reference to this collection’s model.

    model :Todo,

 

// Save all of the todo items under the ‘"todos-backbone"’ namespace.

    localStorage :newBackbone.LocalStorage("todos-backbone"),

 

// Filter down the list of all todo items that are finished.

done:function(){

returnthis.filter(function(todo){

return todo.get(‘done’);

});

},

 

// Filter down the list to only todo items that are still not finished.

    remaining :function(){

returnthis.without.apply(this,this.done());

},

 

// We keep the Todos in sequential order, despite being saved by unordered

//GUID in the database. This generates the next order number for new items.

    nextOrder :function(){

if(!this.length)

return1;

returnthis.last().get(‘order’)+1;

},

 

// Todos are sorted by their original insertion order.

    comparator :function(todo){

return todo.get(‘order’);

}

});

数据模型也可涵盖有方法,比如自己的校验,或者跟后端的通讯、数据的存取等等,在地方两独例证中,也都来反映。

AngularJS的模子定义方式以及Backbone不同,可以不欲通过同重叠封装,直接利用原生的JavaScript简单多少、对象、数组,相对来说比较省事。

控制器

每当Backbone中,是从未单身的控制器的,它的有的控制的职责都位于了视图里,所以实际上就是平等栽MVP(Model
View Presentation)模式,而AngularJS有甚清晰的支配器层。

或者为这个todo为例,在AngularJS中,会时有发生局部预约的注入,比如$scope,它是控制器、模型与视图之间的桥梁。在控制器定义之上,将$scope作为参数,然后,就好在控制器里面为它助长模型的支撑。

functionTodoCtrl($scope){

    $scope.todos =[{

        text :’learn angular’,

done:true

},{

        text :’build an angular app’,

done:false

}];

 

    $scope.addTodo =function(){

        $scope.todos.push({

            text : $scope.todoText,

done:false

});

        $scope.todoText =”;

};

 

    $scope.remaining =function(){

var count =0;

        angular.forEach($scope.todos,function(todo){

            count += todo.done?0:1;

});

return count;

};

 

    $scope.archive =function(){

var oldTodos = $scope.todos;

        $scope.todos =[];

        angular.forEach(oldTodos,function(todo){

if(!todo.done)

                $scope.todos.push(todo);

});

};

}

本例中也$scope添加了todos这个累组,addTodo,remaining和archive三只点子,然后,可以于视图中针对他们进行绑定。

视图

在这些主流的MV*框架中,一般都提供了概念视图的法力。在Backbone中,是如此定义视图的:

// The DOM element for a todo item…

varTodoView=Backbone.View.extend({

//… is a list tag.

    tagName :"li",

 

// Cache the template function for a single item.

template: _.template($(‘#item-template’).html()),

 

// The DOM events specific to an item.

    events :{

"click .toggle":"toggleDone",

"dblclick .view":"edit",

"click a.destroy":"clear",

"keypress .edit":"updateOnEnter",

"blur .edit":"close"

},

 

// The TodoView listens for changes to its model, re-rendering. Since there’s

// a one-to-one correspondence between a **Todo** and a **TodoView** in this

// app, we set a direct reference on the model for convenience.

    initialize :function(){

this.listenTo(this.model,’change’,this.render);

this.listenTo(this.model,’destroy’,this.remove);

},

 

// Re-render the titles of the todo item.

    render :function(){

this.$el.html(this.template(this.model.toJSON()));

this.$el.toggleClass(‘done’,this.model.get(‘done’));

this.input =this.$(‘.edit’);

returnthis;

},

 

//……

 

// Remove the item, destroy the model.

    clear :function(){

this.model.destroy();

}

});

面这个例子是一个独立的“部件”视图,它对界面及之已经来素没有因。也时有发生那么有视图,需要负让界面及之已发生素,比如下面是,它经过el属性,指定了HTML中id为todoapp的元素,并且还当initialize方法中援了另外一些素,通常,需要直接停到界面的顶层试图会动用这种艺术,而“部件”视图一般由主视图来创造、布局。

// Our overall **AppView** is the top-level piece of UI.

varAppView=Backbone.View.extend({

// Instead of generating a new element, bind to the existing skeleton of

// the App already present in the HTML.

    el : $("#todoapp"),

 

// Our template for the line of statistics at the bottom of the app.

    statsTemplate : _.template($(‘#stats-template’).html()),

 

// Delegated events for creating new items, and clearing completed ones.

    events :{

"keypress #new-todo":"createOnEnter",

"click #clear-completed":"clearCompleted",

"click #toggle-all":"toggleAllComplete"

},

 

// At initialization we bind to the relevant events on the `Todos`

// collection, when items are added or changed. Kick things off by

// loading any preexisting todos that might be saved in *localStorage*.

    initialize :function(){

this.input =this.$("#new-todo");

this.allCheckbox =this.$("#toggle-all")[0];

 

this.listenTo(Todos,’add’,this.addOne);

this.listenTo(Todos,’reset’,this.addAll);

this.listenTo(Todos,’all’,this.render);

 

this.footer =this.$(‘footer’);

this.main = $(‘#main’);

 

Todos.fetch();

},

 

// Re-rendering the App just means refreshing the statistics — the rest

// of the app doesn’t change.

    render :function(){

vardone=Todos.done().length;

var remaining =Todos.remaining().length;

 

if(Todos.length){

this.main.show();

this.footer.show();

this.footer.html(this.statsTemplate({

done:done,

                remaining : remaining

}));

}else{

this.main.hide();

this.footer.hide();

}

 

this.allCheckbox.checked=!remaining;

},

 

//……

});

对于AngularJS来说,基本未需来额外的视图定义,它利用的凡一直定义在HTML上之主意,比如:

<divng-controller="TodoCtrl">

<span>{{remaining()}} of {{todos.length}} remaining</span>

<ahref=""ng-click="archive()">archive</a>

<ulclass="unstyled">

<ling-repeat="todo in todos">

<inputtype="checkbox"ng-model="todo.done">

<spanclass="done-{{todo.done}}">{{todo.text}}</span>

</li>

</ul>

<formng-submit="addTodo()">

<inputtype="text"ng-model="todoText"size="30"

placeholder="add new todo here">

<inputclass="btn-primary"type="submit"value="add">

</form>

</div>

以斯例子中,使用ng-controller注入了一个TodoCtrl的实例,然后,在TodoCtrl的$scope中附加的那些变量和章程还足以一直看了。注意到里面的ng-repeat部分,它遍历了todos数组,然后以中的单个todo对象创建了片HTML元素,把相应的价值填到个中。这种做法以及ng-model一样,都创造了双向绑定,即:

l  改变模型可以随时反映到界面上

l  以界面上举行的操作(输入,选择等等)可以实时反映至范里。

还要,这种绑定都见面自动忽略其中可能因为拖欠数据要引起的异常情况。

模板

模板是是时代一样栽特别突出的缓解方案。我们经常有这样的光景:在一个界面上再度展示类似之DOM片段,例如微博。以传统的开发方式,也可以轻松实现出来,比如:

var feedsDiv = $("#feedsDiv");

 

for(var i =0; i <5; i++){

var feedDiv = $("<div class=’post’></div>");

 

var authorDiv = $("<div class=’author’></div>");

var authorLink = $("<a></a>")

.attr("href","/user.html?user=’"+"Test"+"’")

.html("@"+"Test")

.appendTo(authorDiv);

    authorDiv.appendTo(feedDiv);

 

var contentDiv = $("<div></div>")

.html("Hello, world!")

.appendTo(feedDiv);

var dateDiv = $("<div></div>")

.html("发布日期:"+newDate().toString())

.appendTo(feedDiv);

 

    feedDiv.appendTo(feedsDiv);

}

而是用模板技术,这通得更进一步文雅,以常用的沙盘框架UnderScore为条例,实现就段功能的代码为:

var templateStr ='<div class="post">’

+'<div class="author">’

+'<a href="/user.html?user={{creatorName}}">@{{creatorName}}</a>’

+'</div>’

+'<div>{{content}}</div>’

+'<div>{{postedDate}}</div>’

+'</div>’;

vartemplate= _.template(templateStr);

template({

    createName :"Xufei",

    content:"Hello, world",

    postedDate:newDate().toString()

});

也可以这样定义:

<scripttype="text/template"id="feedTemplate">

<% _.each(feeds,function(item){%>

<div class="post">

<div class="author">

<a href="/user.html?user=<%= item.creatorName %>">@<%= item.creatorName %></a>

</div>

<div><%= item.content %></div>

<div><%= item.postedData %></div>

</div>

<%});%>

</script>

 

<script>

$(‘#feedsDiv’).html( _.template($(‘#feedTemplate’).html(), feeds));

</script>

除去,UnderScore还提供了片怪便宜的集结操作,使得模板的运用更为有益于。如果你打算利用BackBone框架,并且需要动用模板作用,那么UnderScore是一个坏好的挑选,当然,也足以选用外的模板库,比如Mustache等等。

万一用AngularJS,可以免需分外的模板库,它自己就是提供了近乎之机能,比如上面这事例可以改写成这样:

<divclass="post"ng-repeat="post in feeds">

<divclass="author">

<ang-href="/user.html?user={{post.creatorName}}">@{{post.creatorName}}</a>

</div>

<div>{{post.content}}</div>

<div>

发布日期:{{post.postedTime | date:’medium’}}

</div>

</div>

主流的模板技术还提供了一部分一定的语法,有些效益很强。值得注意的凡,他们虽然和JSP之类的代码写法类似甚至同,但原理差别很要命,这些模板框架都是当浏览器端执行的,不借助于任何服务端技术,即使界面文件是.html也足以,而传统比如JSP模板是得后端支持之,执行时是当服务端。

路由

常见路由于是概念在后端的,但是在当下看似MV*框架的支援下,路由于得以由前端来分析执行。比如下面这Backbone的路由示例:

varWorkspace=Backbone.Router.extend({

    routes:{

"help":"help",// #help

"search/:query":"search",// #search/kiwis

"search/:query/p:page":"search"// #search/kiwis/p7

},

 

    help:function(){

},

 

    search:function(query, page){

}

});

每当上述例子中,定义了有些路由的映照关系,那么,在实际上访问的时刻,如果以地点栏输入”#search/obama/p2″,就会配合到”search/:query/p:page”这漫漫路由,然后,把”obama”和”2″当作参数,传递让search方法。

AngularJS中定义路由的措施有些区别,它使一个$routeProvider来提供路由的存取,每一个when表达式配置一长长的总长由于信息,otherwise配置默认路由,在布置路由的时光,可以指定一个分外的控制器,用于控制就漫漫路由于相应之html界面:

app.config([‘$routeProvider’,

function($routeProvider){

    $routeProvider.when(‘/phones’,{

        templateUrl :’partials/phone-list.html’,

        controller :PhoneListCtrl

}).when(‘/phones/:phoneId’,{

        templateUrl :’partials/phone-detail.html’,

        controller :PhoneDetailCtrl

}).otherwise({

        redirectTo :’/phones’

});

}]);

留神,在AngularJS中,路由于的template并非一个完好无缺的html文件,而是其中的同样截,文件的头尾都可绝不,也可绝不那些含有的标体制和JavaScript文件,这些当主界面中载入就可以了。

由定义组件

因而过XAML或者MXML的总人口肯定会指向中的但是扩大标签印象深刻,对于前端开发人员而言,基于标签的零件定义方式势必是优化外任何方法的,看下面这段HTML:

<div>

<inputtype="text"value="hello, world"/>

<button>test</button>

</div>

就算是刚接触这种东西的初手,也能够解它们的意思,并且能够按照在做出像样之物,如果下传统的面向对象语言去描述界面,效率远没有这样强,这就算是在界面开发世界,声明式编程比命令式编程适合的最为重点原因。

而是,HTML的价签是零星的,如果我们得的职能不在中,怎么惩罚?在付出进程被,我们恐怕用一个选项卡的功效,但是,HTML里面未提供选项卡标签,所以,一般的话,会用部分li元素和div的组合,加上部分css,来实现选项卡的效果,也有的框架下JavaScript来就这些职能。总的来说,这些代码都不够简洁直观。

苟会来相同种植技术,能够提供类似这样的法子,该多好吗?

<tabs>

<tabname="Tab 1">content 1</tab>

<tabname="Tab 2">content 2</tab>

</tabs>

以AngularJS的首页,可以见到如此一个章节“Create
Components”,在它们的演示代码里,能够见到类似的一样截:

<tabs>

<panetitle="Localization">

        …

</pane>

<panetitle="Pluralization">

        …

</pane>

</tabs>

那,它是怎形成的也罢?秘密在此间:

angular.module(‘components’,[]).directive(‘tabs’,function(){

return{

        restrict :’E’,

        transclude :true,

        scope :{},

        controller :function($scope, $element){

var panes = $scope.panes =[];

 

            $scope.select=function(pane){

                angular.forEach(panes,function(pane){

                    pane.selected =false;

});

                pane.selected =true;

}

 

this.addPane =function(pane){

if(panes.length ==0)

                    $scope.select(pane);

                panes.push(pane);

}

},

template:'<div class="tabbable">’

+'<ul class="nav nav-tabs">’

+'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">’

+'<a href="" ng-click="select(pane)">{{pane.title}}</a>’

+'</li>’

+'</ul>’

+'<div class="tab-content" ng-transclude></div>’

+'</div>’,

        replace :true

};

}).directive(‘pane’,function(){

return{

require:’^tabs’,

        restrict :’E’,

        transclude :true,

        scope :{

            title :’@’

},

        link :function(scope, element, attrs, tabsCtrl){

            tabsCtrl.addPane(scope);

},

template:'<div class="tab-pane" ng-class="{active: selected}" ng-transclude>’+'</div>’,

        replace :true

};

})

眼看段代码里,定义了tabs和pane两个标签,并且限制了pane标签不可知退tabs而独自在,tabs的controller定义了她的行为,两者的template定义了事实上变化的html,通过这种措施,开发者可以扩大出好欲的初因素,对于使用者而言,这不见面多其他额外的担当。

 

相关文章