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
AIMurano和应用HyBird情势的有些开发技术,如PhoneGap。

l  Adobe Flash,Flex,Microsoft Silverlight,Java
Applet,JavaFx等帕杰罗IA开发技术。

正文首要从“web前端”,也即狭义前端的角度出发,以人类科学和技术进步划时期的点子,将前端开发划分为多少个基本点的一时,指引我们精通一下前端那十几年来的升华历程。

 

石器时代

最早期的Web界面基本都以在互连网上应用,人们浏览有个别内容,填写几个表单并且付诸。当时的界面以浏览为主,基本都是HTML代码,大家来看3个最简便易行的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>

在那一个等级,由于客户端和服务端的天职未作显然的划分,比如生成1个字符串,能够由前端的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那七个标签。某一大块有单独成效的界面写到3个HTML文件,然后在主界面里面把它看成贰个frame来载入,一般的B/S系统合二为一菜单的法门都以这般的。是否觉得很熟习?对的,现在大多公司的在那之中系统正是以此时代的产物。

别的,还出现了一部分基于特定浏览器的客户端组件技术,比如IE浏览器的Nokia(HTML
Component)。那种技术最初是为了对已部分常用成分附加行为的,后来有点场地也用它来贯彻控件。微软ASP.NET的一部分本子里,使用这种技术提供了树形列表,日历,选项卡等成效。HUAWEI的帮助和益处是同意用户自行扩充HTML标签,能够在和谐的命名空间里定义成分,然后,使用HTML,JavaScript和CSS来兑现它的布局、行为和观感。这种技术因为是微软的私家技术,所以稳步变得不那么流行。Firefox浏览器布其后尘,也推出过一种叫XUL的技能,也一样没有流行起来。

 

铁器时期

本条时期的彗星是Ajax的面世以及JS基础框架的兴起。

AJAX

AJAX其实是一密密麻麻已有技巧的重组,早在那些名词出现在此之前,那一个技巧的运用就已经相比普遍了,卡那霉素ail因为妥善地应用了那一个技能,获得了很好的用户体验。由于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那样的移位开发库等。

 

农业时代

其方今期的标志性事件是模块加载规范(速龙以及CMD)的现身。

AngularJS,铁器时代现身的底子框架提供了代码的团伙能力,可是不可能提供代码的动态加载能力。动态加载JavaScript为啥首要呢?因为随着Ajax的普及,jQuery等协理库的出现,Web上得以做很复杂的遵守,因而,单页面应用程序(SPA,Single
Page Application)也稳步多了四起。

单个的界面想要做过多功用,须求写的代码是会比较多的,可是,并非全部的功效都急需在界面加载的时候就满门引入,假若能够在急需的时候才加载那多少个代码,就把加载的下压力分担了,在这么些背景下,出现了有的用以动态加载JavaScript的框架,也出现了一部分定义那类可被动态加载代码的正统。

AMD

在那几个框架里,盛名度最高的是RequireJS,服从了速龙(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以外,国内的牛人Taobao玉伯开发了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的服务上,那样,对有个别实体的读写能够立异到数据库中。其它3个特色是,它们一般都提供贰个事变,用于监察和控制数据的变动,这一个机制使得数据绑定成为恐怕。

在局地框架中,数据模型须求在原生的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来说,基本不必要有13分的视图定义,它利用的是平素定义在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  在界面上做的操作(输入,选取等等)可以实时反映到模型里。

再者,那种绑定都会活动忽略当中恐怕因为空数据而滋生的非凡情形。

模板

模板是这些时代一种很优异的解决方案。我们平日有那般的场景:在3个界面上再也展现类似的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是1个很好的选料,当然,也可以选拔其余的模板库,比如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来提供路由的存取,每3个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的价签是不难的,借使大家要求的法力不在个中,怎么做?在支付进程中,大家只怕要求2个选项卡的效率,可是,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,通过这种方式,开发者能够扩大出自身索要的新因素,对于使用者而言,那不会追加别的额外的承负。

 

相关文章