前端开发准备过程(node,npm)

以下环境为Ubuntu 14.10版本。

一、Nodejs

安装Nodejs

建议从nodejs网站下载二进制或源代码来安装,因为apt-get里的版本太旧了。

mkdir ~/nodejs
cd ~/nodejs
wget https://nodejs.org/dist/v4.5.0/node-v4.5.0-linux-x64.tar.xz
xz -d node-v4.5.0-linux-x64.tar.xz
tar xvf node-v4.5.0-linux-x64.tar
ln -s ~/nodejs/node-v4.5.0-linux-x64/bin/node /usr/local/bin/node
ln -s ~/nodejs/node-v4.5.0-linux-x64/bin/npm /usr/local/bin/npm

升级NPM

Node自带npm,npm是一个包管理器,最好用下面的命令升级一下(其中-g参数表示npm包是全局的,如果不加这个参数,表示是只限于当前项目的)。

npm install -g npm

安装NRM

npm官方的源在国内不是很稳定,可以使用nrm简化切换源的操作,nrm的使用方法很简单,可以看这个文章。主要就是nrm ls和nrm use这两条命令。

npm install -g nrm
ln -s ~/nodejs/node-v4.5.0-linux-x64/bin/nrm /usr/local/bin/nrm

nrm use影响~/.npmrc配置文件里的registry配置项,例如nrm use taobao后打开.npmrc文件:

registry=https://registry.npm.taobao.org/

二、开发环境(Atom)

安装Atom

atom.io下载并安装atom编辑器。

安装插件

  • atom-ternjs JS语法提示
  • atom-beautify 用于格式化代码
  • emmet 高效编码
  • file-icons 文件彩色图标
  • highlight-line 代码行高亮
  • highlight-select 高亮选择,双击选择的内容,会自动高亮全篇中的该内容

根据网络条件,在atom里安装插件经常不能成功,可以尝试命令行安装(包名可以在atom插件介绍里看到)。

apm install atom-html-preview

从git克隆代码

git clone git@git.oschina.net:myuser/myproject.git

下载依赖包

项目所依赖的包定义在package.json文件里。在项目所在目录下,执行下面的命令行自动下载和安装它们:

npm install

这个过程超级慢。

若提示“npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.1.2”,可以加“--no-optional”选项解决:

npm install --no-optional

启动前端应用

在项目所在目录执行下面的命令:

npm start

参考链接:

待续

使用Dojo Toolkit中的问题和解决

file

本文记录使用dojo toolkit进行前端开发过程中遇到的问题和解决方法。

1、动态填充dijit.form.MultiSelect

MultiSelect不支持dojo.data(即datastore),所以如果想把一个datastore动态填充进去,只能写一些javascript代码来动态创建那些

2、强制一个datastore从url里取得数据

myDataStore._forceLoad();

3、当datastore含有嵌套结构的时候,datastore.fetch()报错“dojo _48 is undefined”

原因是在得到的json串里包含了"identifier"的声明,但嵌套结构里有一些对象没有包含所声明的属性。解决的方式1、让所有对象都包含该属性,2、去掉"identifier"声明(推荐1,因为我猜去掉这个声明后,在一些控件里将无法映射id值到html代码)参考链接1 参考链接2

4、在dojo中响应事件

请参考:JavaScript events and Dojo

5、刷新BorderContainer

有时需要动态隐藏BorderContainer里的一些区域(如left),若近将该区域的style.display设置为"none",则该区域所在的部分将变为空白,其他区域(如center)不会自动占领它原来的屏幕空间。这时需要调用dojo.byId('myBorderContainer').layout()方法手动让dojo重新计算一次布局,其他布局类似。一个相关链接(仅针对旧版本dojo)

6、用dojo画统计图(柱图,饼图等)

见这个教程:A Beginner's Guide to Dojo Charting (Part1, Part2)

7、程序改变dijit.dialog的标题

dijit.dialog并没有提供一个"setTitle"方法来控制标题内容,但可以通过dijit.byId('myDialog').titleNode.innerHTML='My New Title'来实现。参考链接

8、BorderContainer里各个pane的显示和隐藏

利用dojox.layout.ExpandoPane(尚不确定是否为experimental),使用的例子见这个链接

9、将ItemFileWriteStore序列化为JSON字符串

var foo=dojo.data.ItemFileWriteStore._getNewFileContentString();

10、dojo的XHR(XmlHttpRequest) 里,handleAs可以有以下取值:

  • text (default)
  • json
  • json-comment-optional
  • json-comment-filtered
  • javascript
  • xml

参考链接

11、将DataStore里的数据以XHR方式发给服务器,并处理返回值

在js里这样写:

var data = scenarioEntryStore._getNewFileContentString();
 dojo.rawXhrPost({
  url: "http://127.0.0.1:8080/myapp/myservlet",
  handleAs: "text",
  postData: data,
  headers: {"Content-Type": "application/json"},
  handle: function (data, args) { if (typeof(data) == "error") {
       alert("Error run analysis");
      } else {
       alert("Successed run analysis"); //do with result data
 }
  }
 });

在myservlet里用下面的方法得到js传过来的json字符串,然后可以用任何java的json包来做解析和处理了:

private String readJSONString(HttpServletRequest request) {
    StringBuffer json = new StringBuffer();
    String line = null; try {
        BufferedReader reader = request.getReader(); while ((line = reader.readLine()) != null) {
            json.append(line);
        }
    } catch (Exception e) {
        System.out.println(e.toString());
    } return json.toString();
}

12、使用xhr的时候,如果不是在本机访问,可能会遇到“Access to Restricted URI Denied”的提示。

一个可能的原因是xhr里的url的domain与访问者浏览器(或其他什么,这里没搞很清楚)的domain不一致,如果是这个原因,只要在url里使用相对路径就可以了。比如原来url是http://127.0.0.1:8080/myapp/myservlet的,改为myservlet即可。如果不是这个原因,这个链接可以参考一下。

13、dojo里的单击事件是“onClick”,写成“onclick”不认。(但onchange是全小写,够奇怪)

14、把json格式的数据画到dojo chart

下面是一个把json数据转换到数组的例子,来自这个链接,实际上是利用prototype的能力。

//JSON:
str = “{values: [1,2,3,4,5]}”; //using prototype
obj = str.evalJSON(); //the array is then referenced by:
chart1.addSeries(”Series 1″, obj.values);

下面这种方式则直接使用了json格式(来自这里),但关于valueFn这个参数的使用方法我还没找到一个比较完整的介绍:

<div dojoType="dojox.charting.widget.Chart2D" id="chart4" theme="dojox.charting.themes.PlotKit.green" style="width: 300px; height: 300px;">
  <div class="plot" name="default" type="Pie" radius="100" fontColor="black" labelOffset="-20"></div>
  <div class="series" name="Series C" store="tableStore" valueFn="Number(x)"></div>
  <div class="action" type="Tooltip"></div>
  <div class="action" type="MoveSlice" shift="2"></div>
</div> 

15、可伸缩的pane

使用ExpandoPane(//测试链接TODO)即可实现。要注意的是,ExpandoPane在dojo 1.3之前的版本里有bug,现象是包含抽屉pane时在IE下最下面的抽屉会被挤掉,在Firefox里则正常。dojo 1.3解决了这个问题。

(如无特别说明,以上均针对dojo 1.3版本。 )

(以下为dojo 1.6版本,dojo总是让人抓狂,一些好不容易获得的经验不得不快记下来)

16、 dojo chart的legend(图例)必须在chart.refresh()以后创建或refresh(),否则图例里不会正确显示颜色和形状,而是都为"X"。(参考链接

17、 在tabcontainer里画的chart,refresh()的时候所在tab页是隐藏状态,切换过去后chart的尺寸不能充满tabcontainer区域。

还不知道正确的方法是什么,一个权宜之计是在tab切换到chart所在tab时,调用一下chart的resize()方法,例如下面这样:

dojo.subscribe("myTabContainer-selectChild", function(child){ if(child.id=="myTab")
        myChart.resize();
});

关于DWR、XMLHTTP、XML-RPC和Flex

今天看到一篇文章,介绍DWR(Direct Web Remoting),它的作用是在javascript里通过iframe直接调用Java类中的方法,可以实现像Google Suggest那种在文本框中输入时自动完成的功能(例如输入“what is the best”),很酷哦。

联想起前一阵项目中级联下拉菜单的问题,使用这种技术也是一种不错的解决方法。实现和DWR类似功能的还有XMLHTTP和XML-RPC,前者可以看作是微软对request/response的一种包装,主要用于从远程服务器取得数据;后者也是一个十分简单的协议,通过在request/response中增加xml格式的信息达到调用远程方法的目的,但需要专用的XML-RPC服务器或在现有服务器中加入对XML-RPC的支持。

要说在Web应用程序中耍酷,不得不提现在越来越流行的Macromedia的Flex。它在你现有系统结构的基础上增加一个Flex服务器,用来把xml格式的代码转换为.swf文件(也就是flash了),让你的表现层变得异常丰富,不仅外观漂亮,还能实现如拖放、渐变以及各种动画效果,用广告词来说就是“提升用户体验”。如果你看过Macromedia网站上提供的案例,相信你很难不被打动。唯一的遗憾就是Flex的价格实在太贵了:至少买双CPU的License,价格$12,000!

和商业产品相比,虽然开源的RIA可能存在一些不足,但至少在小项目中用用问题不大,Laszlo就是其中之一。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/15/77496.html

Web页中级联下拉选择框问题的解决方法

示范中心项目里有一些页面要求几个下拉选择框的内容是具有关联的关系的,例如在编辑一个实验项目时,要先在一个下拉框里选择该项目所在的示范中心,这时实验室下拉框里的内容要根据用户选中的示范中心改变。为了实现这个目的,我们先后想了几种方法。

1、在用户选择示范中心时,刷新页面,并把示范中心代码加在url的后面传给action。这样的最大问题是,如果用户已经填写了一部分表单,在更改示范中心选择的时候会丢失已填写的信息。除非在刷新时把整个表单的所有域都加在url里传回,但这样做要在jsp页面里增加非常多的逻辑,因此不能使用。

2、把与示范中心关联的几个下拉框作为iframe的方式,当选择了一个示范中心时,根据所选的示范中心重新load这个iframe,这样不会影响到其他可能已经填写的域。这样做的问题是,iframe对应的页面里要重复考虑权限问题,这是不能忍受的,同时和所有使用框架技术的应用一样,有可能造成一些页面不一致的情形。

3、前面两种方式虽然都被否决了,但它们共同的好处是在用户改变对示范中心的选择时,数据是动态加载的。第三种方式是把所有数据一次读到客户端,使用javascript的方法实现关联。我在网上找到了一些js代码,但大部分都不适合动态生成数据,这也是一开始没有使用js方式的原因之一。不过最后还是找到了一个我认为不错的js,对它进行少量修改后比较漂亮的完成了任务,下面就说说具体的方法。

这个js的演示和下载地址在http://fason.nease.net/samples/select/,这里我要感谢这位fason朋友提供这么好的工具。在需要关联的页面里,例如编辑实验项目的页面,首先要引入xselect.js这个script,然后构造一个Dsy对象,使用这个对象的add方法增加关联下拉框的各个选项。还有两个数组,sel是关联下拉框的id数组,def是这些下拉框缺省的选项值数组。最后使用attachSelect函数将dsy对象和这两个数组关联起来就大功告成。对于选项是动态生成的情况,就是要动态生成这么一段javascript,如下所示:

<script language="JavaScript" src="../xselect.js"></script>
<script language="JavaScript">
<!--
var dsy = new Dsy();
var sel = ["demoCenter","lab"];
var def = ['<bean:write name="projectForm" property="demoCenterCode"/>','<bean:write name="projectForm" property="labCode"/>'];

dsy.add("0",[['<bean:message key="select.prompt" bundle="root"/>','']
<logic:iterate name="<%=DemoCenter.class.getName()%>" id="dc">
    ,['<bean:write name="dc" property="name"/>','<bean:write name="dc" property="code"/>']
</logic:iterate>
]);

dsy.add("0_0",[['<bean:message key="select.prompt" bundle="root"/>','']]);
<logic:iterate name="<%=DemoCenter.class.getName()%>" id="dc" indexId="index">
    dsy.add('0_<%=index.intValue()+1%>',[['<bean:message key="select.prompt" bundle="root"/>','']
    <bean:define id="code" name="dc" property="code" type="String" />
    <logic:iterate name="<%=Lab.class.getName()%>" id="lab">
        <logic:equal name="lab" property="demoCenter.code" value="<%=code%>">
            ,['<bean:write name="lab" property="name"/>','<bean:write name="lab" property="code"/>']
        </logic:equal>
    </logic:iterate>
    ]);
</logic:iterate>
attachSelect(dsy,sel,def);
//-->
</script>

生成的静态页面就是下面这样,页面显示后会缺省选择“北京大学数学示范中心”和“软件工程实验室”:

<script language="JavaScript" src="../xselect.js"></script>
<script language="JavaScript">
<!--
var dsy = new Dsy();
var sel = ["demoCenter","lab"];
var def = ['10000001','10000007'];

dsy.add("0",[['--------请选择--------','']
    ,['教育部','10000000']
    ,['北京大学数学示范中心','10000001']
    ,['北京师范大学普通话示范中心','10000002']
    ,['北京邮电大学卫星通信示范中心','10000003']]);
dsy.add("0_0",[['--------请选择--------','']]);
dsy.add('0_1',[['--------请选择--------','']
        ,['生物实验室','10000004']
        ,['软件工程实验室','10000007']
]);
dsy.add('0_2',[['--------请选择--------','']
        ,['数据挖掘实验室','10000002']
        ,['微电子实验室','10000003']
        ,['Web服务实验室','10000006']
]);
dsy.add('0_3',[['--------请选择--------','']
        ,['航空航天实验室','10000001']
        ,['湍流实验室','10000005']
        ,['互联网实验室','10000008']
]);
dsy.add('0_4',[['--------请选择--------','']
]);    
attachSelect(dsy,sel,def);
//-->
</script>

不过这个js有个小问题,就是它的def数组中的每个元素只能是一个字符串,当选择框是可多选的列表框时,就不能实现多个选项的预选择(populate)。为了解决这个问题,我对xselect.js进行了少量的修改,允许def中的每个元素是一个数组。以下是修改后的xselect.js文件中的doChange()函数:

function doChange(v) {
    var str = "0";
    for (var i = 0; i < v; i++) { str += ("_" + Sel[i].selectedIndex); };
    var ss = Sel[v];
    if (oDsy.Exists(str)) {
        with (ss) {
            length = 0;
            var ar = oDsy.Items[str], xx = 0;
            for (var i = 0; i < ar.length; i++) {
                var ot = ar[i][0], ov = ar[i][1] ? ar[i][1] : ot;
                //if (ov == Store[v]) xx = i;
                options[i] = new Option(ot, ov);

                //Added by zhanghao
                if(Store[v] instanceof Array){
                    for(var j=0;j<Store[v].length;j++)
                        if(Store[v][j] == ov)options[i].selected = true;
                }else{
                    if(ov == Store[v])options[i].selected = true;
                }
            }
            //options[xx].selected = true;
            if (++v < Sel.length) doChange(v);
        } 
    } else {
        for (var i = v; i<Sel.length; i++) {
            with (Sel[i]) {
                length = 0;
                options[0] = new Option("--", "");
                options[0].selected = true;
            }
        }
    }
}

其中注释掉了两句,并增加了一段对数组元素的处理,目前这个js在示范中心系统里工作良好。为了让jsp页面中的代码进一步减少,我们今后还要把动态生成js的代码写为tag的方式。

Web页中级联下拉选择框问题是一个非常普遍的问题,本贴里介绍的是我认为十分圆满的一个客户端方式的解决方法,用户在使用时不会感到任何不便,而且因为数据权限问题是在action里已经解决的,在对应的jsp页面里所做的任何处理都不需要考虑数据权限了。但在一些数据量特别大的系统里要考虑网络连接速度是否适合将所有数据传到客户端的问题。

Update: 相关代码下载

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/11/15/64006.html

反向链接referrer的原理

一直都不明白反向链接是怎么实现的,今天好像有点懂了。在这个网站看到,原来一段javascript代码就够了,一定是利用了浏览器的什么功能。

<script language="Javascript" src="http://www.downes.ca/referrers.js"></script>

倒,看了一下这个js的内容,原来document还有个referrer属性啊,现在完全没有神秘感了。

function write_ref() {
   document.write("<script language='Javascript' src='http://216.55.133.99/cgi-bin/data/referrers.cgi?in=" + document.referrer + "&out=" + document.location + "'>");
   document.write("</");
   document.write("script>");
}
write_ref();

不过博客园这里好像是不让插入script的,插入的script会被过滤。而且也不能插入form,因为会和搜索的那个form嵌套。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/25/36529.html