上一篇文章介绍了ngSlide插件,接下来就该为大家介绍它是如何被制作出来的了。
指令
我们看到这个插件的基本用法就是
<div slider></div>
那么这里的slider是angularJs中的指令名,指令是用来做HTML扩展的,我在上一文的示例
<div slider>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
</div>
中并没有写ul,但它实际上在浏览器里的运行效果却是这样的
<div class="cc" slider>
<ul class="cc">
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
</ul>
</div>
那么多出来的这些代码就是由angular的指令生成的了,创建一个js文件,命名为ngSlide.js,书写代码如下:
angular.model("ngSlide",[])
.directive("slider",[function(){
return{
restrict: 'A',
replace: true,
template:"" +
"<div class='cc tibooslider'>" +
'<ul class="cc" ></ul>' +
"</div>"
}
}])
这里ngSlide的是你的ng-app名,restrict指的是指令的声明方式,其实A是指属性方式,如果将其定义为E的话会更加语义化,可以这样调用
<slider><slider>
但是因为这种方式对IE6和7不太友好,所以我写成了A,replace则是指是否覆盖你声明指令的代码,template就是你要覆盖的代码了,
拥有了这段代码以后,你只要在你的index.html中写如下代码:
<html ng-app="ngSlide">
<body ng-controller="slidecon">
<div slider>
</div>
<script src='angular.js'>
<script src='ngSlide.js'>
<script>
angular.module("ngSlide",[])
.controller("slider",function($scope){
});
</script>
</body>
</html>
就会自动被浏览器渲染成
<div class="cc" tibooslider>
<ul class="cc" ></ul>
</div>
transclude
接下来还要生成那些li,因为图片的地址和上面的文字每次使用插件的时候是不一样的,这是属于“配置”的一部分,这些应该被放在声明里面.将index.html改为
<html ng-app="ngSlide">
<body ng-controller="slidecon">
<div slider>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
</div>
<script>
angular.module("ngSlide",[])
.controller("slider",function($scope){
});
</script>
</body>
</html>
刷新浏览器,...什么都没有发生,因为浏览器并不知道要将这些li插入到模板的什么地方去,这时就需要用到我们的transclude了。将ngSlide.js修改为
angular.model("ngSlide",[])
.directive("slider",[function(){
return{
restrict: 'A',
replace: true,
transclude:true,
template:"" +
"<div class='cc tibooslider'>" +
'<ul class="cc" ng-transclude></ul>' +
"</div>"
}
}])
这里就很简单明了啦,当浏览器看到transclude声明时就会去寻找模板里带ng-transclude的地方把声明中带的标签包含进去。至此ngSlide的HTML部分就渲染完毕了。
幻灯效果
接下来还需要为ngSlide制作幻灯切换效果,最好的办法是用css动画自己重写一遍,因为angualr的watch机制能让你减少很多代码,不过因为想要快一点搭建完自己的UI库以及兼容IE8所以我选择了我常用的一个jquery幻灯插件SuperSlide。虽然这样会让这部分代码显得有点“脏”(原则上来说angular项目是不推荐引入jquery的)
scope
angular不允许在控制器中操作DOM,而幻灯动画这种事情就是赤裸裸的DOM操作了,它必须写在drective里面,通常来说我调用我的jquery插件时是这样的
$(id).slide({mainCell:ulid});
可以看到我需要2个参数,一个是幻灯最外层div的id,一个是ul的id,而ul是由angular自动生成的,我决定只让用户输入外层id一个参数就可以了,这时需要用到directive的scope,修改ngSlide.js代码如下
angular.model("ngSlide",[])
.directive("slider",[function(){
return{
restrict: 'A',
replace: true,
scope:{
id:"@id"
},
transclude:true,
template:"" +
"<div class='cc tibooslider'>" +
'<ul class="cc" ng-transclude></ul>' +
"</div>"
}
}])
id:"@id"指的是把指令中的id属性赋值给scope.id,当用户再HTML中输入以下代码时
<div slider id="slideid">
scope.id就会自动被复制为slideid,这时需要的参数都有了,那么原则上来讲我只要在directive里面调用我的jquery插件就可以完成这个伟业了。修改ngSlide.js代码如下:
angular.model("ngSlide",[])
.directive("slider",[function(){
return{
restrict: 'A',
replace: true,
scope:{
id:"@id"
},
transclude:true,
template:"" +
"<div class='cc tibooslider'>" +
'<ul class="cc" ng-transclude id="{{ulid}}"></ul>' +
"</div>",
link:function(elem,scope,attr){
var id = "#" + scope.id
var ulid = id + "ul";
$(id).slide({mainCell:ulid});
}
}
}])
刷新浏览器,运行,。。。。什么变化都没有,这里有一个深坑,原因我们来做一下实验,在link函数里面把elem给输出出来看一下:
console.log(elem[0].innerHTML);
结果如下:
<div class='cc tibooslide' slider id='slideid'>
<ul class='cc' ng-transclude id="{{ulid}}">
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
<li>
<img src="1.jpg" alt=""/>
<p>标题</p>
</li>
</ul>
</div>
注意这个id="{{ulid}}",在这里ul的id数据根本就没有被渲染出来,在angular中model的渲染完成是在directive的link函数执行以后的。这可就愁白了我的头发了,如果使用$timeout的话因为用户的网速不一致我并不知道要延迟多久再加载这段代码,而angular偏偏没有提供directive加载完成的事件,这个问题足足困扰了我3天。
解决方案
这个问题的正解是将jquery插件用一个不设定参数的$timeout给包裹起来,这样就一定会在id值被渲染出来以后才执行它,造成这种情况的原因是js是阻塞运行的,$timeout只会在空闲时才会执行里面的代码,修改ngSlide代码如下:
angular.model("ngSlide",[])
.directive("slider",['$timeout',function($timeout){
//别忘了依赖注入
return{
restrict: 'A',
replace: true,
scope:{
id:"@id"
},
transclude:true,
template:"" +
"<div class='cc tibooslider'>" +
'<ul class="cc" ng-transclude id="{{ulid}}"></ul>' +
"</div>",
link:function(elem,scope,attr){
$timeout(function(){
var id = "#" + scope.id
var ulid = id + "ul";
$(id).slide({mainCell:ulid});
})
}
}
}])
至此程序就能正常运行了,当然接下来还要做少许的配置,例如说插件需要向左向右的箭头该怎么办呀,这部分的代码大家可以移步我的github查看,https://github.com/RenShine/ngSlide。下一篇文章我将会介绍这个插件的SCSS部分是如何实现的