tomcat发布后访问路径问题

近来一直发现一个问题,tomcat工程发布后路径不对,用http://localhost:8080/Urban/login.jsp报404,无法访问。

这是tomcat的server.xml文件配置

<Context docBase="chengguanfuwuqi" path="/Urban" reloadable="false" source="org.eclipse.jst.jee.server:chengguanfuwuqi"/></Host>

查阅资料都说,docBase是工程的存放路径,可以写成绝对路径,相对路径是指%TOMCAT_HOME%/webapps/下,path是指虚拟的访问路径。这么说来http://localhost:8080/Urban/login.jsp是没有问题的。

如果还是出现404的错误,且前面配置没有错,可能就是在%TOMCAT_HOME%/conf/web.xml文件中把虚拟路径显示目录给禁止啦,此时可以在tomcat的web.xml文件中找到:

 <servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

将listings的value改为true,然后重新启动tomcat,在输入http://localhost:8080/Urban/login.jsp,测试成功!
如果项目开发完成,准备部署在服务器上时,记住要把web.xml文件中参数listings的值改为false,这样可以避免把项目的部署路径呈现给使用者!

IntellJ IDEA生成war包

点击项目名,按F4

enter description here
之后生成一个如下图所示的信息,注意红框中的路径,即就是最后生成war包的路径

enter description here
点击Build->Build Artifacts

enter description here
弹出如下框,选择刚才生成的项目,点击build,之后即可在tomcat的webapps下看到war包。

enter description here

enter description here

SpringMVC后台接受不到POST请求数据

[TOC]

项目代码合并更新之后遇到一个让我这几天及其崩溃的事情,Spring MVC的Controller里面使用了@RequestParam注解来接收参数,但是只在GET请求的时候才能正常访问,在使用POST请求的时候会产生找不到参数的异常。原本好好的POST请求开始报400错误,找不到REST服务,一般情况下报这种错误多是由于POST请求时传递的参数不一致,但是这次不存在这种问题,百思不得其解啊。。。网上各种资料显示,可能问题出现在jquery发送的ajax请求上。刚好恶补了解一下这方面内容。(PS:经过两天的折腾,尝试换Tomcat,IDE之后,发现是环境的问题,之前用的eclipse配置了破解的JRebel,可能影响到了,到第三天终于解决,神啊。。。)

ajax提交

jQuery:发送的数据时用&符号连接起来的,如下:

enter description here
对应的Content-Type是 application/x-www-form-urlencoded。如下:

enter description here

AngularJS:传输数据使用的Content-Type是:application/json格式,对应的格式为{“pageNow”:1,”pageSize”:5,”type”:””}

数据的格式一定要和Content-Type保持一致。

form提交

form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和multipart/form-data,默认为application/x-www-form-urlencoded。 当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…),然后把这个字串append到url后面,用?分割,加载这个新的url。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server。 如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。 但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。

注解

@RequestBody

该注解用于读取Request请求的body部分数据

@ResponseBody

该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
当返回的数据不是html的页面,而是其他某种格式的数据时(如json、xml等)使用;

@RequestMapping produces

@RequestMapping(value = "/upload",produces="application/json")  

produces:方法仅处理request请求中Accept头中包含了”application/json”的请求,同时暗示了返回的内容类型为application/json;

springMVC取值方式

@PathVariable

通过注解@PathVariable获取url中的值。

@RequestMapping(value="user/{id}/{name}",method=RequestMethod.GET)
public String myController(@PathVariable String id,@PathVariable String name, ModelMap model) {
     ……
      return "ok";
  }

@RequestParam

通过注解RequestParam获取传递过来的值。

@RequestMapping(value = "/test", method = RequestMethod.POST) 
public String myTest(@RequestParam("name") String name,@RequestParam("phone") String phone, ModelMap model) { 
     ……
     return "ok";
 }

HttpServletRequest

通过原生HttpServletRequest获取值。

@RequestMapping(value="/test" method = RequestMethod.POST) 
public String get(HttpServletRequest request, HttpServletResponse response) { 
     String name = request.getParameter("name")); 
     return "ok"; 
 }

ModelAttribute

通过注解ModelAttribute直接映射表单中的参数到POJO。

补充

  • application/x-www-form-urlencoded: 窗体数据被编码为名称/值对。这是标准的编码格式。空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值。
  • multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。 不对字符进行编码,使用二进制数据传输,一般用于上传文件,非文本的数据传输。

Spring如果要接受这种数据,添加以下配置

<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">       
</bean>
  • text/plain: 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符。

单页面应用

单页Web应用(single page web application,SPA),顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面上完成,这一切都由JavaScript来控制。因此,单页Web应用会包含大量的JavaScript代码,复杂度可想而知,模块化开发和设计的重要性不言而喻。
单页面应用指浏览器中运行的引用,在使用期间不会重新加载页面,可以理解为从服务器加载的一个富客户端。优势在于:即拥有左面应用的及时性又拥有网站的可移植性和可访问性。
传统的网站应用大多数的业务逻辑大多在服务器端,并且对于用户的输入响应,必须等待一个“请求、响应、重绘”的一个周期循环。而单页应用可以像桌面应用一样尽可能的把业务数据处理过程转移到浏览器端,把数据验证、授权、持久存储必须要放在服务器端,把响应时间减至最小。
随着单页web应用的崛起,各种框架不断涌现,如Angular.js、backbone.js、ember.js等,还有Require.js等模块加载器。

enter description here

单页web应用是什么?它又会给传统网站带来哪些好处?

bootstrap-fileinput之文件上传

[TOC]

此功能实现使用bootstrap-fileinput(fileinput.min.js)插件,主页地址:

  1. bootstrap-fileinput Github
  2. 主页

    MultipartFile

    Spring使用MultipartFile来进行多文件上传,需要配置一个multipartResolver的bean

    @RequestMapping(value=”/parseAttr.json”, method = RequestMethod.POST)
    public ModelMap parseAttr(@RequestParam(“file”) MultipartFile uFile) {

    ModelMap result = new ModelMap();
    ...
    result.put("keys",set);
    return result;
    

    }

    常用方法

    MultipartFile file = new MultipartFile();
    file.getOriginalFilename():获取上传文件的原名
    file.getInputStream():获取文件流

form表单

分两类上传:办事指南、典型案例。

<form role="form" class="form-horizontal" id="importFile" method="post" accept-charset="utf-8" enctype="multipart/form-data">
    <div class="form-group">
        <label class="col-lg-2 control-label" for="descption">文章类型</label>
        <div class="col-lg-9">
            <select name="descption" id="descption" style="padding: 6px 12px;line-height: 1.42857143;    border: 1px solid #ccc;    border-radius: 4px;">
                <option value="1">办事指南</option>
                <option value="2">典型案例</option>
            </select>
        </div>
    </div>
    <div class="form-group">
        <label class="col-lg-2 control-label" for="fileInput">文章上传</label>
        <div class="col-md-9">

            <input id="fileInput" name="fileInput" type="file" multiple
                class="file-loading">
        </div>
    </div>

    <hr />
    <div class="btn-group" style="margin-left: 160px;margin-right:5px">
        <button type="button" class="btn btn-success" id="submitOK">
            <i class="glyphicon glyphicon-check">提交</i>
        </button>
        <button type="button" class="btn btn-danger" data-dismiss="modal"
            id="btn-close">
            <i class="glyphicon glyphicon-remove">关闭</i>
        </button>
    </div>
</form> 

js代码

$("#fileInput").fileinput({
    uploadUrl:"#",//上传的地址
    uploadAsync: false,
    language : "zh",//设置语言
    showCaption: true,//是否显示标题
    showUpload: false, //是否显示上传按钮
    browseClass: "btn btn-primary", //按钮样式 
    allowedFileExtensions: ["html","htm"], //接收的文件后缀
    maxFileCount: 1,//最大上传文件数限制
    maxFileSize: 1000,//最大上传文件大小
    uploadAsync: true,
    previewFileIcon: '<i class="glyphicon glyphicon-file"></i>',
    allowedPreviewTypes: null, 
    previewFileIconSettings: {
        'doc': '<i class="fa fa-file-word-o text-primary"></i>',
        'xls': '<i class="fa fa-file-excel-o text-success"></i>',
        'ppt': '<i class="fa fa-file-powerpoint-o text-danger"></i>',
        'jpg': '<i class="fa fa-file-photo-o text-warning"></i>',
        'pdf': '<i class="fa fa-file-pdf-o text-danger"></i>',
        'zip': '<i class="fa fa-file-archive-o text-muted"></i>',
        'htm': '<i class="fa fa-file-code-o text-info"></i>',
        'txt': '<i class="fa fa-file-text-o text-info"></i>',
        'mov': '<i class="fa fa-file-movie-o text-warning"></i>',
        'mp3': '<i class="fa fa-file-audio-o text-warning"></i>',
    },
    previewFileExtSettings: {
        'doc': function(ext) {
            return ext.match(/(doc|docx)$/i);
        },
        'xls': function(ext) {
            return ext.match(/(xls|xlsx)$/i);
        },
        'ppt': function(ext) {
            return ext.match(/(ppt|pptx)$/i);
        },
        'zip': function(ext) {
            return ext.match(/(zip|rar|tar|gzip|gz|7z)$/i);
        },
        'htm': function(ext) {
            return ext.match(/(php|js|css|htm|html)$/i);
        },
        'txt': function(ext) {
            return ext.match(/(txt|ini|md)$/i);
        },
        'mov': function(ext) {
            return ext.match(/(avi|mpg|mkv|mov|mp4|3gp|webm|wmv)$/i);
        },
        'mp3': function(ext) {
            return ext.match(/(mp3|wav)$/i);
        },
    }
});

上传文件可附加参数,通过uploadExtraData参数来实现。

uploadExtraData: function() {
    var extraValue = null;
    var radios = document.getElementsByName('excelType');
    for(var i=0;i<radios.length;i++){
        if(radios[i].checked){
            extraValue = radios[i].value;
        }
    }
    return {"excelType": extraValue};
}

上传成功的回调函数:

 $("#fileInput").on("fileuploaded", function(event, data, previewId, index) {
    alertView("上传成功!");
}); 

上传失败的回调函数:

$('#excelFile').on('fileuploaderror', function(event, data, previewId, index) {
    alertView("上传文件不正确!");
});

js提交带文件表单

 $("#submitOK").click(function() {
    $.ajax({
        cache: true,
        type: "POST",
        url: "rest/guide/importfile",
        data: new FormData($('#importFile')[0]),// 你的formid
        processData: false,
        contentType: false,
        error: function(request) {
            alert("上传失败");
        },
        success: function(data) {
            if(data){                    
                alert("上传成功");
            }
            else {
                alert("上传失败");
            }
        }
    });
}); 

需要注意的以下几点:

  • processData设置为false。因为data值是FormData对象,不需要对数据做处理。
  • 标签添加enctype=”multipart/form-data”属性。
  • cache设置为false,上传文件不需要缓存。
  • contentType设置为false。因为是由
    表单构造的FormData对象,且已经声明了属性enctype=”multipart/form-data”,所以这里设置为false。
  • 上传后,服务器端代码需要使用从查询参数名为file获取文件输入流对象,因为input中声明的是name=”file”。

上传文件后台

@RequestMapping(value = "/importfile", method = RequestMethod.POST)
@ResponseBody
public boolean importfile(HttpServletRequest request, HttpServletResponse response) throws Exception {
    request.setCharacterEncoding("utf-8");
    final String allowFileSuffix = "html,htm";
    String basePath = request.getSession().getServletContext().getRealPath("/");
    String realpath = null;

    // 检查输入请求是否为multipart表单数据。
    if (ServletFileUpload.isMultipartContent(request)) {
        DiskFileItemFactory dff = new DiskFileItemFactory();//获得磁盘文件条目工厂  
        dff.setSizeThreshold(1024000);// 指定在内存中缓存数据大小,单位为byte
        ServletFileUpload upload = new ServletFileUpload(dff);// 创建该对象
        upload.setFileSizeMax(5000000);//指定单个上传文件的最大尺寸
        upload.setSizeMax(10000000);// 指定一次上传多个文件的总尺寸
        upload.setHeaderEncoding("utf-8");
        List<FileItem> fileItems = new ArrayList<FileItem>();
        try {
            fileItems = upload.parseRequest(request);
        } catch (FileUploadException e1) {
            logger.error("文件上传发生错误" + e1.getMessage());
            return false;
        }
        String fullPath = null;
        String fileName = null;
        String fullName=null;
        String type="";
        for (FileItem fileItem : fileItems) {

            // 判断该表单项是否是普通类型
            if (fileItem.isFormField()) {
                String name = fileItem.getFieldName();// 控件名
                String value = fileItem.getString(); //控件值        
                if (name.equals("descption")) {
                    if(value.equals("1")){
                        type="guide";//办事指南
                    }
                    else if(value.equals("2")){
                        type="case";//典型案例
                    }
                    realpath=basePath+type;
                    File tmpDir = new File(realpath);// 初始化上传文件的临时存放目录
                    if (!tmpDir.exists()) {
                        tmpDir.mkdirs();
                    }
                    dff.setRepository(tmpDir);// 指定上传文件的临时目录

                }
            } else {
                String filePath = fileItem.getName();
                if (filePath == null || filePath.trim().length() == 0)
                    continue;
                fullName = filePath.substring(filePath.lastIndexOf(File.separator) + 1);
                fileName = filePath.substring(0,filePath.lastIndexOf("."));

                String extName = filePath.substring(filePath.lastIndexOf(".") + 1);
                fullPath = realpath + File.separator + fullName;
                if (allowFileSuffix.indexOf(extName) != -1) {
                    try {
                        fileItem.write(new File(fullPath));
                        //上传文件地址存入数据库
                        Guide guide = new Guide();
                        guide.setName(fileName);
                        guide.setUrl("../"+type +"/" + fullName);
                        if(type.equals("guide")){                                
                            guide.setType(1);
                        }
                        else if(type.equals("case")){
                            guide.setType(2);
                        } 
                        guideServiceImpl.insert(guide);
                    } catch (Exception e) {
                        logger.error("文件写入Guide表出错");
                        return false;
                    }
                } else {
                    throw new FileUploadException("文件格式不正确");
                }
            }
        }
    }
    return true;
}

js中this的用法

[TOC]

即使网上有很多关于js中this的用法详解,我还是想写一篇属于自己的理解,以方便自己日后回顾。
常见的用法分为以下四种:

代表全局变量

var x = 0;
function test(){
    this.x = 1;
}

test();
alert(x); // 1

作为构造函数

这里所说的构造函数是指用函数new一个新对象,即:

var t = new test();

这时,this指这个新对象

var x = 2;
function test(){
    this.x = 1;
}

var t = new test();
alert(t.x); // 1
alert(x);   // 2

作为对象的方法调用

这时this值上级对象

function test(){
    alert(this.x); // 2
    this.x = 1;
    alert(this.x); // 1
}

var t = {};
t.x = 2;
t.o = test;
t.o();

apply

apply是函数对象的一个方法,作用是改变函数的调用对象,它的第一个参数就是改变后调用这个函数的对象,this就指的是这个对象。如果参数为空,默认调用全局对象

var x = 3
function test(){
    alert(this.x);
}

var t = {};
t.x = 2;
t.o = test;
t.o(); //2
t.o.apply(); //3
t.o.apply(t); //2 相当于t.o();

apply的详解查看js的原型及继承实现一文

Spring集成Log4j和Slf4j

[toc]

Log4j的集成

Spring项目中集成Log4j

  1. 下载好log4j的jar包(例如:log4j-1.2.11.jar)
  2. web.xml
<!--log4j配置文件加载-->  
<context-param>      
   <param-name>log4jConfigLocation</param-name>      
   <param-value>/WEB-INF/log4j.properties</param-value>      
</context-param>  
<!--启动一个watchdog线程每1800秒扫描一下log4j配置文件的变化--> 
<!-- log4j 配置扫描刷新间隔 可以不用 --> 
<context-param>      
   <param-name>log4jRefreshInterval</param-name>      
   <param-value>1800000</param-value>      
</context-param>   

<!--spring log4j监听器-->  
<listener>      
   <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>      
</listener> 

只配置listener也可以

  1. log4j.properties
    日志的主配置文件
log4j.rootLogger=info,stdout,debug,error    
log4j.logger.org.springframework=info
#log4j.logger.org.springframework.web=debug
log4j.appender.stdout=org.apache.log4j.ConsoleAppender    
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout    
log4j.appender.stdout.layout.ConversionPattern=[%-5p] [%d{HH\:mm\:ss}] %c - %m%n    

log4j.logger.info=info    
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender    
log4j.appender.info.layout=org.apache.log4j.PatternLayout    
log4j.appender.info.layout.ConversionPattern=[%-5p] [%d{HH\:mm\:ss}] %c - %m%n    
log4j.appender.info.datePattern='.'yyyy-MM-dd    
log4j.appender.info.Threshold = INFO    
log4j.appender.info.append=true    
log4j.appender.info.File=${catalina.home}/logs/log4j/info.log
log4j.appender.warn.File=${catalina.home}/logs/log4j/warn.log    

log4j.logger.debug=debug    
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender    
log4j.appender.debug.layout=org.apache.log4j.PatternLayout    
log4j.appender.debug.layout.ConversionPattern=[%-5p] [%d{HH\:mm\:ss}] %c - %m%n    
log4j.appender.debug.datePattern='.'yyyy-MM-dd    
log4j.appender.debug.Threshold = DEBUG    
log4j.appender.debug.append=true    
log4j.appender.debug.File=${catalina.home}/logs/log4j/debug.log

log4j.logger.warn=warn    
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender    
log4j.appender.warn.layout=org.apache.log4j.PatternLayout    
log4j.appender.warn.layout.ConversionPattern=[%-5p] [%d{HH\:mm\:ss}] %c - %m%n    
log4j.appender.warn.datePattern='.'yyyy-MM-dd    
log4j.appender.warn.Threshold = DEBUG    
log4j.appender.warn.append=true    

log4j.logger.error=error    
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender    
log4j.appender.error.layout=org.apache.log4j.PatternLayout    
log4j.appender.error.layout.ConversionPattern=[%-5p] [%d{HH\:mm\:ss}] %c - %m%n    
log4j.appender.error.datePattern='.'yyyy-MM-dd    
log4j.appender.error.Threshold = ERROR    
log4j.appender.error.append=true    
log4j.appender.error.File=${catalina.home}/logs/log4j/error.log
  1. 代码中写入日
private static final Logger logger = LoggerFactory.getLogger(XXX.class);
logger.info("XXXX");
logger.warn("XXXX");
logger.error("XXXX");

Slf4j的集成

Slf4j不是一个真正的日志实现,而是一个抽象层,它允许我们在后台使用任意一个日志类库。所以以后更换为其它日志工具时,只要修改配置文件,不用修改代码,所以开源框架常用到Slf4j。
spring 4默认是不依赖slf4j的,只依赖common logging.
集成好Log4j之后就可直接使用Slf4j了

  1. 加入slf4j的jar包
  2. 配置同上Log4j的配置

即集成好Log4j之后就可直接使用Slf4j了。
使用:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;
...
private static final Logger logger = LoggerFactory.getLogger(User.class);  

Spring Restful web services

Spring 是流行的 Java EE 应用开发框架,现在它的 MVC 层也支持 REST 了,
在Spring框架支持REST之前,人们会选用其他几种技术来实现java的RESTful Web Services,如REStlet,Jersey和RESTEasy。
Spring3.0之后增加了对RESTful Web Services的支持。REST 支持被无缝整合到 Spring 的 MVC 层,它可以很容易应用到使用 Spring 构建的应用中。
Spring REST 支持的主要特性包括:

  • 注释,如 @RequestMapping 和 @PathVariable,支持资源标识和 URL 映射
  • ContentNegotiatingViewResolver 支持为不同的 MIME/内容类型使用不同的表示方式
  • 使用相似的编程模型无缝地整合到原始的 MVC 层

    实现

    web.xml中激活Spring,配置:
listener-ContextLoadListener
servlet-DispatcherServlet

Spring的配置文件

<!--指明 controller 所在包,并扫描其中的注解-->
<context:component-scan base-package="com.aerors.th.gis"/>
<!-- 开启注解 -->
<mvc:annotation-driven/>
<!-- 用注解进行依赖注入 -->
<context:annotation-config></context:annotation-config>

component-scan: 启用对带有 Spring 注释的类进行自动扫描
在实践中,它将检查控制器类中所定义的 @Controller 注释

Controller控制器实现

@Controller
@RequestMapping("/user")
public class UserRest {
    @Resource
    private IUserService userManagerImpl;

    @RequestMapping(value = "/queryAllUser", method = RequestMethod.GET)
    @ResponseBody
    public JSONArray queryAllUser() {

        List<User> dataList = userManagerImpl.queryAll();
        JSONArray userJSONList2 = JSONArray.fromObject(dataList);
        return userJSONList2;
    }

@PathVariable与@RequestParam

@Controller
public class EmployeeController {
    @RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
    public ModelAndView getEmployee(@PathVariable String id) {
        Employee e = employeeDS.get(Long.parseLong(id));
        returnnew ModelAndView(XML_VIEW_NAME, "object", e);
    }
    ...
}


@Controller
@RequestMapping("employees")
public class EmployeeController {

    Employee employee = new Employee();

    @RequestMapping(value = "/{name}", method = RequestMethod.GET, produces = "application/json")
    public @ResponseBody Employee getEmployeeInJSON(@PathVariable String name) {

        employee.setName(name);
        employee.setEmail("employee1@genuitec.com");

        return employee;

    }

    @RequestMapping(value = "/queryByName", method = RequestMethod.POST)
    @ResponseBody
    public User queryByName(@RequestParam("uname") String uname) {

        User user;
        try {
            user = userManagerImpl.queryByName(uname).get(0);
        } catch (Exception e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
            return null;
        }

        return user;
    }
}

@RestController

Spring 4.0中引入 @RestController简化了@Controller,@ResponseBody注释
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。

@RestController
@RequestMapping("employees")
public class EmployeeController {

    Employee employee = new Employee();

    @RequestMapping(value = "/{name}", method = RequestMethod.GET, produces = "application/json")
    public Employee getEmployeeInJSON(@PathVariable String name) {

        employee.setName(name);
        employee.setEmail("employee1@genuitec.com");

        return employee;

    }
}

angular用户注册及密码一致性验证

注册表单

初学习angular,用一个简单的用户注册页面来练练手,mark一下,以后需要就直接Ctrl+c了。

<form action="" class="form-horizontal" name="regForm"     ng-controller="regController" 
 ng-submit="submitForm()" novalidate>
    <div class="form-group" ng-class="{'has-success':regForm.username.$valid&&regForm.username.$touched,'has-error':regForm.username.$invalid&&regForm.username.$touched }">
        <label for="" class="col-md-2 control-label">
            用户名
        </label>
        <div class="col-md-9">
            <input type="text" 
            class="form-control" 
            ng-model="user.username" 
            name="username" 
            ng-minlength="4" ng-maxlength="10" 
            placeholder="用户名" 
            required>
            <p class="help-block has-error" ng-show="regForm.username.$error.required&&regForm.username.$touched">用户名不能为空</p>
            <p class="help-block has-error" ng-show="regForm.username.$error.minlength||regForm.username.$error.maxlength">长度为4-10位</p>
        </div>
    </div>
    <div class="form-group" ng-class="{'has-success':regForm.password.$valid&&regForm.password.$touched,'has-error':regForm.password.$invalid&&regForm.password.$touched}">
        <label for="" class="col-md-2 control-label">
            密码
        </label>
        <div class="col-md-9">
            <input type="password" 
            class="form-control" 
            ng-model="user.password" 
            name="password" 
            ng-pattern="/^[A-Za-z]{1}[0-9A-Za-z_]{2,29}$/"
            ng-minlength="4" ng-maxlength="10" 
            placeholder="只能是数字、字母和下划线 " 
            required>
            <p class="help-block has-error" ng-show="regForm.password.$error.required
            &&regForm.password.$touched">密码不能为空</p>
            <!--正则验证-->
            <p class="help-block has-error" ng-show="regForm.password.$error.pattern&&regForm.password.$touched">
            只能是数字、字母和下划线</p>
            <p class="help-block has-error" 
            ng-show="regForm.password.$error.minlength||regForm.password.$error.maxlength">密码为4-10位</p>
        </div>
    </div>
    <div class="form-group" ng-class="{'has-success':regForm.confirmPassword.$valid&&regForm.confirmPassword.$touched,'has-error':(regForm.confirmPassword.$invalid&&regForm.confirmPassword.$touched)||(user.password!=user.confirmPassword&&regForm.confirmPassword.$touched)}">
        <label for="" class="col-md-2 control-label">
            确认密码
        </label>
        <div class="col-md-9">
            <input type="password" 
            class="form-control" 
            ng-model="user.confirmPassword" 
            name="confirmPassword" 
            ng-minlength="4" ng-maxlength="10" 
            ng-pattern="/^[A-Za-z]{1}[0-9A-Za-z_]{2,29}$/"
            placeholder="只能是数字、字母和下划线" 
            required>
            <p class="help-block has-error" ng-show="regForm.confirmPassword.$error.required&&regForm.confirmPassword.$touched">确认密码不能为空</p>
            <!--正则验证-->
            <p class="help-block has-error" ng-show="regForm.confirmPassword.$error.pattern&&regForm.confirmPassword.$touched">只能是数字、字母和下划线</p>
            <p class="help-block has-error" ng-show="regForm.confirmPassword.$error.minlength||regForm.confirmPassword.$error.maxlength">确认密码为4-10位</p>
            <p class="help-block has-error" 
            ng-show="user.password!=user.confirmPassword&&regForm.confirmPassword.$touched">两次密码不一致</p>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-9">
            <button class="btn btn-primary" ng-disabled="regForm.$invalid">登陆</button>
        </div>
    </div>
</form>

bootstrap的使用

[toc]

bootstrap model远程加载

<a id="adduser" class="btn btn-danger" data-toggle="modal"    href="addUser.html" data-target="#adduserModal">添加</a>
<div class="modal fade" id="adduserModal" role="dialog" aria-labelledby="myModalLabel" data-keyboard="true">
    <div class="modal-dialog" role="document">
    <div class="modal-content"></div>
    </div>
</div>

table表中修改某一条记录时,model弹出框获取记录值

function editTerminal(row,index) {
    index1=index;
    $('#editTerminalModal').on('show.bs.modal',function(event) {
        var button = $(event.relatedTarget);
        var recipient = button.data('whatever');
        var modal = $(this);
        modal.find('.modal-title').text(recipient);
        modal.find('#tid').val(row.tid);
        modal.find('#type').val(row.type);
        modal.find('#cid').val(row.cid);
        modal.find('#uid').val(row.uid);
        modal.find('#date').val(row.date);
    })
}
|