一、背景介绍
如果我们想实现图片上传功能,其中需要包含以下功能
*点击按钮上传图片
*图片能在本地预览
*显示图片信息,显示上传进度
*点击上传按钮上传到服务器
*点击删除按钮,删除。
*上传按钮只能按一次
现在我们需要把它用directive自定义指令封装起来。
为什么要封装呢?这个图片上传插件如果需要多次运用的话,用自定义指令封装后能减少大量代码。
二、知识剖析
关于指令,网上有很多资料,这里就简单说一下。
angular指令本质上就是AngularJs扩展具有自定义功能的html元素的途径。
angular指令本质上就是AngularJs扩展具有自定义功能的html元素的途径。
内置指令,打包在AngularJs内部的指令,所有内部指令的命名空间 都使用ng作为前缀,所以在写自定义指令的时候,避免用ng作为指令命名的前缀。
创建指令的方式有四种,在指令里用 restrict属性控制:
*E:元素
*A:属性
*C:css类
*M:注释
向指令中传递数据,用template属性
directive 在使用隔离 scope 的时候,提供了三种方法同隔离之外的地方交互:
*@ 绑定一个局部 scope 属性到当前 dom 节点的属性值。结果总是一个字符串,因为 dom 属性是字符串。
*= 通过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间建立双向绑定。
*& 提供一种方式执行一个表达式在父 scope 的上下文中。如果没有指定 attr 名称,则属性名称为相同的本地名称。(其实说白了,就是可以使用在父scope中定义的函数。)
replace:是否用模板替换当前元素。
true : 将指令标签替换成temple中定义的内容,页面上不会再有标签;
false :则append(追加)在当前元素上,即模板的内容包在标签内部。默认false。
三、常见问题
如何实现封装?
四、解决方案
见代码
五、编码实战
//template
<form name="myForm" class="col-lg-10">
<div class="row">
<div class="col-lg-10 col-sm-12">
<div class="row">
<label class="col-md-2 col-sm-12 titles">{{labelName}}</label>
<div class="col-lg-12" ng-if="uploader">
<label class="btn-pic" for="{{imgId}}" style="float:left;">
选择文件
</label>
<input type="file" accept="image/*"
name="file" id="{{imgId}}"
style="display: none;"
nv-file-select=""
uploader="uploader"
multiple="">
<label style="line-height:.3rem;">(图片最大为1m)</label>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<!--图片预览-->
<img style="max-width: 100%;" ng-src="{{imageUrl}}">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-10">
<!-----上传图片插件----->
<table class="table">
<thead>
<tr>
<th>图片名</th>
<th>文件大小</th>
<th>进度</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in uploader.queue">
<td style="word-break:break-all">
<strong>{{ item.file.name }}</strong>
</td>
<td nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td>
<td>
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar" role="progressbar"
ng-style="{ 'width': item.progress + '%' }"></div>
</div>
</td>
<td class="text-center">
<span ng-show="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
<span ng-show="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
<span ng-show="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
</td>
<td>
<button type="button" class="btn btn-success btn-xs" ng-click="item.upload()"
ng-disabled="item.isReady || item.isUploading || item.isSuccess">
<span class="glyphicon glyphicon-upload"></span> Upload
</button>
<button type="button" class="btn btn-warning btn-xs" ng-click="item.cancel()"
ng-disabled="!item.isUploading">
<span class="glyphicon glyphicon-ban-circle"></span> Cancel
</button>
<button type="button" class="btn btn-danger btn-xs" ng-click="item.remove()">
<span class="glyphicon glyphicon-trash"></span> Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
//directive
angular.module("myApp")
.directive("uploadFile", function (FileUploader) {
return {
restrict: "EA",
templateUrl: "html/fileUpload.html",
scope: {
labelName:"@",
imageUrl:"=ngModel",
imgId:"@"
},
replace: true,
link: function (scope,ele,attrs) {
// 上传图片插件
scope.uploader = new FileUploader({
url: "/carrots-admin-ajax/a/u/img/task",
queueLimit: 1
});
scope.uploader.onSuccessItem = function (fileItem, response,status,headers) {
scope.imageUrl = response.data.url;
};
}
//link或者controller都可以达到效果
// controller:function ($scope) {
// // 上传图片插件
// $scope.uploader = new FileUploader({
// url: "/carrots-admin-ajax/a/u/img/task",
// queueLimit: 1
// });
// $scope.uploader.onSuccessItem = function (fileItem, response,status,headers) {
// $scope.imageUrl = response.data.url;
// };
// }
}
});
//html
<upload-file ng-model="params.img" img-id="img" label-name="article图片"></upload-file>
六、扩展思考
指令中controller跟link的区别?当我们每次想要扩展个自定义指令时,应该用哪个?
简单来说,优先使用link。事实上,这两个都可以获取到作用域,元素,属性等引用,也都会执行一次。
控制器可以暴露一个API,而link可以通过require与其他的指令控制器交互。
所以如果要开放出一个API给其他指令用就写在controller中,否则写在link中。
七、更多讨论
讨论点一、去掉template页中第7行的「ng-if="uploader"」,使用link就会报错,使用controller则正常,这是为什么?
讨论点二、如何上传多张图片?
讨论点三、&方法该如何应用?
八、参考文献
参考一:angular自定义指令详解