Angularjs web应用
背景
随着mvvm逐渐成熟,现在使用jQuery构建web应用已经显得过时了,而且使用jQuery需要编写更多的代码去控制dom的取值、赋值、绑定事件等,而mvv从底层实现了对以上操作的支持,让程序员可以从原始的复杂、重复的编码中解放出来,让程序员可以将更多的重心放在业务的实现、数据的交互上去,而且大大减少了程序员需要编写的代码量。
多年来的web经验告诉我们,编写web应用最难的地方主要在于浏览器的兼容问题,而且更多的兼容问题主要体现在ie上,因此只要能解决ie上的问题,那么这个web应用的其他问题都不再是问题了。
今天,我将使用angualrjs来构建兼容浏览器的web应用,其实主要是解决ie下的一些问题,大致如下:
- ie下构建app
- ie下a标签unsafe问题
- ie下ngSrc无法正确绑定问题
- ie下ngMouseleave的问题
- ie下创建service的一些问题
- ie下使用swfupload的一些问题
这里将使用angular的1.0.7版本,这是由于该版本对于ie低版本的支持较好,且存在的BUG较少,而高版本在ngShow、ngHide、ngBindHtml等标签上都会出现严重的问题。
ie下构建app
在chrome、ff等浏览器下,我们可以直接在html或者body标签上直接绑定ngApp来绑定angularjs的应用,但是在ie下是无法实现的,这是由于低版本的ie不支持html5的一些特性以及JSON对象,因此我们需要引入2个js来解决这个问题:
有了以上2个js文件后,ie下绑定ngApp的方式也略微有点区别,需要有class、ngApp、id的支持,代码如下:
<body class="ng-app:myApp" id="ng-app" ng-app="myApp">
ie下a标签unsafe问题
当a标签的href绑定有效的url时,无法触发绑定其他事件,如:click,但是如果没有href或者href为空,在ie下则会出现unsafe的问题,因此需要给href绑定javascript:void(0),这样既能解决unsafe的问题又能解决无法触发事件的BUG。
ie下ngSrc无法正确绑定问题
在ie下,不管是使用ng-src或者ng-src="{{ xx }}"都无法让img显示图片,而且在低版本ie下使用属性="{{xx}}"都是无法实现你想要的功能的,因此只能使用自定义的指令去实现某些不支持的标签。
这里我自己实现了一个customSrc的指令去解决ie下ngSrc的问题,大致代码如下:
myApp.directive('customSrc', function () {
return {
scope: {
data: '='
},
link: function (scope, element, attrs) {
scope.$watch('data', function (v) {
if (!v)
return; element.attr('src', v);
});
}
};
});
ie下ngMouseleave的问题
其实这不算是ng-mouseleave的bug,而是ie下,对于mouseleave和mouseover的表现不同而已,首先我们来看一段html代码,如:
<div ng-click="display=!display">下拉单</div>
<div ng-show="display" ng-mouseleave="display=false">
<div>标题</div>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
</div>
在chrome、ff等浏览器下,这个脚本运行的还是很顺利的,但是在ie下会出现一个问题,就是当鼠标移入ul的时候,下拉单的列表层会消失,但是在ul内加上ng-mouseover="display=true"以后,这个BUG就可以解决了
ie下创建service的一些问题
当我在创建模块的时候,如果模块只有一个方法时,我一般会直接返回这个函数,示例代码为:
//seajs
module.exports = function (){
//coding
}; //angular
myApp.service('$fn', function (){
return function (){
//coding
};
});
在ie下,$fn()是无法正确调用的,会出现异常,因此需要做些许调整,代码如:
//angular
myApp.service('$fn', function (){
return {
exec: function (){
//coding
}
};
});
在ie下使用$fn.exec()就不会出现问题了。
ie下使用swfupload的一些问题
使用swfupload在低版本的ie下,最常出现的问题是flash的版本问题,因此首先得保证ie下flash的版本。
偶尔会遇到"例外被抛出且未被接住",出现该异常的时候,主要注意2个问题:
1、后台返回数据的时候,需要设置响应流的Head的ContentType为text/html; charset=UTF-8
2、ie下绑定swfupload的button_placeholder的元素不能包含在ng-repeat内,如:
//html
<li ng-repeat="m in imgs">
<a href="javascript:void(0)">
<img data="m" width="178" height="178" custom-src />
</a>
</li>
当我们要将最后一个li的元素绑定到swfupload的button_placeholder上时,低版本ie下就会出现以上的错误,因此在使用当中要注意。
结尾
以上是我在开发兼容浏览器angular应用遇到的一些问题以及解决方案,如果你有更好的解决方案,请告诉我。
那么今天的文章就到这里了,如果文章中有什么问题请告诉我,谢谢!
背景
当我们开发一个Web项目的时候,为了将图片管理与web服务分离开,通常都会搭建一个图片服务器。
之所以选择nodejs是因为使用nodejs来搭建web项目相当简单而且快速,虽然这个图片服务器很简单,也有很多人会认为使用nodejs来当图片服务器不合适,但是当我们的应用没有达到非常大的程度的情况下,其实nodejs还是够用的。
会使用到的工具如下:
- nodejs
- express(nodejs mvc框架)
- body-parser(express middleware)
- gm(nodejs中用来处理图片的)
- uuid(nodejs中用于生成uuid)
- underscore(nodejs数据处理库)
- ImageMagick(gm会调用该程序处理图片)
那么接下来就来尝试实现这个简易的图片服务器吧, ^_^
搭建项目
首先要使用express来搭建项目,由于图片服务器的功能相对简单,只有2个功能:1、获取图片资源 2、上传图片,因此对应express只需要使用到bodyParser这样的组件,代码大致如下:
//app.js
var app = require('express')();
process.app = app;//方便在其他地方使用app获取配置 require('./config')(__dirname, app);//所有配置
var mode = app.get('mode');
require('./controller/' + mode + 'Controller.js'); var config = app.get(mode);
require('http').createServer(app).listen(config.port, function () {
console.log('%s已经启动,正监听:%s', config.name, config.port);
}); //config.js
var path = require('path'); var OPTIONS = {
targetDir: function (app) {
return path.join(app.get('rootDir'), 'img');
},
read: {
name: '<<图片服务器(读取)>>',
port: 80,
'default': 'default.jpg',
sizeReg: /(\w+)-(\d+)-(\d+)\.(\w+)$/
},
save: {
name: '<<图片服务器(保存)>>',
port: 9990
},
mode: 'read',
contentType: {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png'
}
}; module.exports = function (rootDir, app) {
app.set('rootDir', rootDir); var $ = require('underscore');
$.each(OPTIONS, function (v, k) {
app.set(k, typeof v === 'function' ? v(app) : v);
}); app.use(require('body-parser')())
};
上面独立出了config.js是为了将所有的配置放在一起方便维护,其次配置中的mode是为了区分当前是一个读取还是存储服务器。
获取图片
这是图片服务器的其中一个功能,用户根据url获取图片服务器内存储的图片,当后台接收这个请求后,首先判断图片上会否存在,如果存在则返回对应的图片否则返回默认的图片,大致代码如下:
var path = require('path');
var fs = require('fs');
var gm = require('gm').subClass({ imageMagick: true });//默认的情况下 gm使用的是另外一个图片处理程序 var app = process.app;
var config = app.get('read');
var targetDir = app.get('targetDir'); app.get('/:filename', function (req, res) {
var filePath = path.join(targetDir, req.params.filename);
fs.exists(filePath, function (exists) {
res.sendfile(exists ? filePath : path.join(targetDir, config.default));
});
});
使用以上的代码便可以对图片进行读取了,但是只能对targetDir目录下的文件进行读取且没有对文件类型进行判断,对文件类型的判断比较简单只要在方法执行之前对文件扩展名进行判断即可,至于增加了文件夹结构的话,跟直接从targetDir目录下读取是差不多的流程,稍微调整一下代码:
var contentTypes = app.get('contentType'); app.get('/:filename', function (req, res) {
sendFile([], req.params.filename, res);
}); app.get('/:folder/:filename', function (req, res) {
sendFile([req.params.folder], req.params.filename, res);
}); app.get('/:folder1/:folder2/:name', function (req, res) {
sendFile([req.params.folder1, req.params.folder2], req.params.filename, res);
}); function sendFile(folders, filename, res) {
var ext = path.extname(filename).substr(1);
if (!contentTypes[ext])
return res.sendfile(getFilePath()); folders.push(filename);
var filePath = getFilePath(path.join.apply(path, folders));
fs.exists(filePath, function (exists) {
res.sendfile(exists ? filePath : getFilePath());
});
} function getFilePath(filename) {
return path.join(app.get('targetDir'), filename || config.default);
}
接下来假设一个场景,如果上传了一张800*600的图片,但是在页面上显示的时候,也许我只想显示一张200*150的缩略图,这时候gm就该登场了,我们可以使用gm来为800*600的图片临时生成一张200*150的图片,并以buffer的形式回传给客户端,大致代码如下:
//判断请求图片是否存在
if (exists)
return res.sendfile(filePath);
var groups = filename.match(config.sizeReg);
if (!groups)
return res.sendfile(getFilePath());
folders[len] = groups[1] + "." + groups[4];
filePath = getFilePath(path.join.apply(path, folders));
var width = parseInt(groups[2]);
var height = parseInt(groups[3]);
//判断原始图是否存在
fs.exists(filePath, function (exists) {
if (!exists)
filePath = getFilePath();
var gm = gm(filePath);
if (width > 0 && height > 0)
gm.resize(width, height);
gm.toBuffer(function (err, buffer) {
if (err)
return res.sendfile(getFilePath());
res.set('Content-Type', contentTypes[ext]);
res.send(buffer);
});
});
上传图片
由于图片服务器只提供最简单的功能,支持文件上传,但是并不会对上传的文件制作水印以及其他的处理,也不会将此功能开放到外网,因此图片上传服务器是在内网的,只能从web服务器那边处理图片以后上传到图片服务器,图片服务器对其进行存储并返回存储后的图片路径,大致代码如下:
var buf = require('buffer');
var fs = require('fs');
var path = require('path');
var util = require('util');
var gm = require('gm').subClass({ imageMagick: true });
var uuid = require('uuid'); var app = process.app;
var contentTypes = app.get('contentType'); /*
请求包含如下参数:
@ext 图片扩展名
@buffer 图片buffer数据
@folder 文件夹,格式:/aa/bb
*/
app.post('/', function (req, res) {
var ext = req.body.ext;
var buffer = req.body.buffer;
if (!(ext && buffer && contentTypes[ext]))
return res.json({ success: false }); var pathArgs = req.body.folder.replace(/\n/g, '');
if (pathArgs)
pathArgs = pathArgs.substr(1).split('/');
else
pathArgs = ['']; createDir(pathArgs); pathArgs.push('');
var filePath = createPath(pathArgs, ext);
gm(new buf.Buffer(JSON.parse(buffer))).write(filePath, function (err) {
if (err)
return res.json({ success: false }); res.json({ success: true, data: util.format('\\%s.%s', pathArgs.join('\\'), ext) });
});
}); function createDir(pathArgs) {
if (pathArgs[0]) {
var dir = path.join(app.get('targetDir'), path.join.apply(path, pathArgs));
var exists = fs.existsSync(dir);
if (!exists)
fs.mkdirSync(dir);
}
} function createPath(pathArgs, ext) {
var args = [app.get('targetDir')];
pathArgs[pathArgs.length - 1] = uuid.v1().replace(/-/g, '');
args.push.apply(args, pathArgs);
var filePath = path.join.apply(path, args) + '.' + ext;
return fs.existsSync(filePath) ? createPath(pathArgs, ext) : filePath;
}
结尾
到这里我们就完成了一个nodejs版本的图片服务器了,因为尽可能将不需要的功能剥离到了其他的项目中去,因此图片服务器的功能就变得很简单、单一了。
对于图片获取服务器生成临时大小的文件,可以先缓存起来,并结合leasing模式进行管理,频繁使用的情况下可以翻倍增加它的租约,到期则直接释放。
而图片存储服务器,如果对于某些应用上传的图片都会生成固定系列大小的情况下, 可以使用gm对原始图进行二次处理,根据规则批量生成各种大小的图,至于图片的名字可以采用一些规则,这样返回的话,依然是存储后的原始图,而且格式则可以通过获取服务器获取,无需再进行缓存了。
由于大部分的代码已经提供了,请不要再跟我要什么源码之类的哦,大家可以自己尝试搭建一下,今天就到这里了,如果有什么问题请告知我,谢谢各位。
Angularjs web应用的更多相关文章
- AngularJs + Web API 页面开发(一)
AngularJS这个JS框架是个神马东东我也不太清楚,我也是初学者~~ AngularJS适用于single page App,单页面程序都是局部刷新的,这一点和Ajax有第一的区别,因为使用Aja ...
- 《Node.js+MongoDB+AngularJS Web开发》读书笔记及联想
总体介绍 <Node.js+MongoDB+AngularJS Web开发>,于2015年6月出版,是一本翻译过来的书,原书名为<Node.js,MongoDB and Angula ...
- 25个超有用的 AngularJS Web 开发工具
AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,改善了JavaScript. 下面我要说的就是25个超有用的AngularJS工具, ...
- 构建兼容浏览器的Angularjs web应用
背景 随着mvvm逐渐成熟,现在使用jQuery构建web应用已经显得过时了,而且使用jQuery需要编写更多的代码去控制dom的取值.赋值.绑定事件等,而mvv从底层实现了对以上操作的支持,让程序员 ...
- [Angularjs]asp.net mvc+angularjs+web api单页应用
写在前面 最近的工作一直在弄一些h5的单页应用,然后嵌入到app的webview中.之前一直在用angularjs+html+ashx的一套东西.实在是玩腻了.然后就尝试通过asp.net mvc的方 ...
- angularJS web应用SEO
javascript给网站带来丰富的用户体验,越来越多的网站开始应用angularjs/emberjs这类MVC来开发web应用,可以说能够使用native方式来看法的手机app基本都可以使用替代的j ...
- [Angularjs]asp.net mvc+angularjs+web api单页应用之CRUD操作
写在前面 前篇文章整理了angularjs学习目录,有园子里的朋友问我要这方面的demo,周末也没什么事,就在之前的单页应用的demo上面添加了增删改查的操作.代码比较简单,这里只列举比较重要的代码片 ...
- 数据绑定和第一个AngularJS Web应用
<!DOCTYPE html> <html lang="en" ng-app> <head> <meta charset="UT ...
- AngularJS web应用程序
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
随机推荐
- 单机部署redis主从备份
redis为了避免单点故障,也支持主从备份.个人在做主从备份的实验时,因为机器数量有限,一般非常少有多台机器做支撑. 本文就将叙述怎样在一台电脑上实现redis的主从备份. 同一台机器上部署多个red ...
- oracle10g获得Date类型字段无分,秒的解决方案!
一般的数据库中,DATE字段只表示日期,不包含日期信息,而Oracle数据库中的DATE数据类型是包含日期.时间的,对于不同的Oracle jdbc驱动版本号.对于该问题的处理都有些差别. 近期使用 ...
- 如何使用Maven创建web工程(详细步骤)
使用eclipse插件创建一个web project 首先创建一个Maven的Project例如以下图 我们勾选上Create a simple project (不使用骨架) 这里的Packing ...
- Cocos2d-x3.0 RenderTexture(一) 保存
.h #include "cocos2d.h" #include "cocos-ext.h" #include "ui/CocosGUI.h" ...
- hdu Simpsons’Hidden Talents(kmp)
Problem Description Homer: Marge, I just figured out a way to discover some of the talents we weren’ ...
- Git 命令速查表
Git 命令速查表 1.常用的Git命令 命令 简要说明 git add 添加至暂存区 git add-interactive 交互式添加 git apply 应用补丁 git am 应用邮件格式补丁 ...
- 数独 (dfs)
自从2006年3月10日至11日的首届数独世界锦标赛以后,数独这项游戏越来越受到人们的喜爱和重视.据说,在2008北京奥运会上,会将数独列为一个单独的项目进行比赛,冠军将有可能获得的一份巨大的奖品—— ...
- 首先运行application的name相应的类或做activity中间name相应的类?
今天找到该程序条目中找到以下两个条件name我写了一个测试程序,一般如以下: 看mainfest.xml <application android:allowBackup="true& ...
- Linux命令学习篇0——原产地
昨天在用curl发送简单的HTTP请求做測试的时候发现自己每次使用的时候都是在网络上查看别人的演示样例才干想起来怎么用,这样效率太低了.尽管有网络依旧在,可是总感觉不是被自己掌握着,心里不踏实,回忆起 ...
- [转]HttpClient使用详解
Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且 ...