精华内容
下载资源
问答
  • 添加到主屏幕页面有什么不同呢? 如何判断web应用是否从桌面图标启动 这就要说到web应用添加到桌面后的显示模式了,一共有这么多种,通过mainfest来控制。只要知道启动模式是什么,就能判断出是否从桌面启动。 ...

    在pwa中介绍过,目前安卓和ios的浏览器都支持将网页添加到主屏幕或者桌面快捷方式,即为webapp。

    公司要求没有添加主屏幕的给弹框提示添加主屏幕。所以需要判断网页是否添加到主屏幕。添加到主屏幕的页面有什么不同呢?

    如何判断web应用是否从桌面图标启动

    这就要说到web应用添加到桌面后的显示模式了,一共有这么多种,通过mainfest来控制。只要知道启动模式是什么,就能判断出是否从桌面启动。

    fullscreen: 全屏显示, 所有可用的显示区域都被使用, 并且不显示状态栏chrome standalone: 让这个应用看起来像一个独立的应用程序,包括具有不同的窗口,在应用程序启动器中拥有自己的图标等。这个模式中,用户代理将移除用于控制导航的UI元素,但是可以包括其他UI元素,例如状态栏。 minimal-ui: 该应用程序将看起来像一个独立的应用程序,但会有浏览器地址栏。 样式因浏览器而异。 browser: 这是默认的设置。该应用程序在传统的浏览器标签或新窗口中打开,具体实现取决于浏览器和平台。

    注意:不管判断ios还是安卓都是通过mainfest里面的来控制的。这个文件很重要webapp必须设置的文件。

    IOS桌面图标启动

    navigator.standalone //为true,则webapp启动

    安卓桌面图标启动

    
    window.matchMedia('(display-mode: standalone)').matches 
    //注意这里的display-mode和mainfest文件的display对应。
    function isStandalone() {
        //alert("ios:"+navigator.standalone+";  Android:"+(window.matchMedia('(display-mode: standalone)').matches)+";  Android:"+(window.matchMedia('(display-mode: fullscreen)').matches))
        return navigator.standalone || (window.matchMedia('(display-mode: fullscreen)').matches)||(window.matchMedia('(display-mode: standalone)').matches);
      }
    
    //由于本人所用mainfest文件可能是display:fullscreen,所以增加(window.matchMedia('(display-mode: fullscreen)').matches)判断

    另外增加浏览器判断:

    if(window.matchMedia('(max-width: 768px)').matches){
                console.log("isStandalone",isStandalone());
                if(isStandalone()){//是webapp不展示
                    setshow(0)
                }else{
                    let AddHomeClose = _utils.getCookie("AddHomeClose")
                    if(AddHomeClose && AddHomeClose!=undefined){
                        setshow(0)
                    }else{
                        setshow(1)
                        const userAgent= navigator.userAgent.toLowerCase()
                        console.log(userAgent);
                        if(userAgent.indexOf("iphone")!=-1){
                            //苹果手机
                            console.log("苹果手机")
                            if (userAgent.match(/MicroMessenger/i) == "micromessenger") {//微信浏览器
                                console.log("微信浏览器")
                                settype(1)
                            }
                        }else{
                            if(userAgent.match(/micromessenger/i) == 'micromessenger'){//微信浏览器
                                settype(2)
                            }else{
                                settype(3)
                            }
                        
                        }
                    }
                }
            }else{
                setshow(0) 
            }

     

    展开全文
  • 怎样在GridView底部添加按钮

    千次阅读 2016-07-23 15:00:11
    但是,当我们内容很多的时候,超出一个页面时,linearlayout就只能显示GridView的数据的,底部的两个按钮会因为GridView已经充满屏幕而不显示。 解决方法: 把GridView设置为不可滑动,这个要自定义一个类来继承...

    如图,右边是一个GridView,底部有两个按钮。当Gridview内容比较少的时候,我们之间用个线性布局就可以达到这种效果。

    但是,当我们内容很多的时候,超出一个页面时,linearlayout就只能显示GridView的数据的,底部的两个按钮会因为GridView已经充满屏幕而不显示。

    解决方法:

    把GridView设置为不可滑动,这个要自定义一个类来继承GridView。测量它的高度,把高度设为尽可能大。、

    代码如下:

    public class NoScrollGridView extends GridView {
    
       public NoScrollGridView(Context context) {
          super(context);
    
       }
    
       public NoScrollGridView(Context context, AttributeSet attrs) {
          super(context, attrs);
       }
    
       @Override
       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
          super.onMeasure(widthMeasureSpec, expandSpec);
       }
    
    }
    然后把GridView和两个按钮写在同一个线性布局里面。再在外面嵌套一个
    ScrollView。就达到想要的效果了
    <ScrollView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.78"
        android:scrollbars="none">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <com.wanwu.power.adapter.NoScrollGridView
                android:id="@+id/gridview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:horizontalSpacing="6dp"
                android:numColumns="2"
                android:scrollbars="none"></com.wanwu.power.adapter.NoScrollGridView>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="22dp"
                android:layout_marginRight="22dp"
                android:layout_marginTop="36dp">
    
    
                <TextView
                    android:id="@+id/btn_all_open"
                    android:layout_width="0dp"
                    android:layout_height="36dp"
                    android:layout_marginLeft="22dp"
                    android:layout_marginRight="22dp"
                    android:layout_weight="1"
                    android:background="@drawable/btn_bg_blue_15"
                    android:gravity="center"
                    android:text="全部开启"
                    android:textColor="#ffffff"
                    android:textSize="14sp" />
    
                <TextView
                    android:id="@+id/btn_all_close"
                    android:layout_width="0dp"
                    android:layout_height="36dp"
                    android:layout_marginLeft="22dp"
                    android:layout_marginRight="22dp"
                    android:layout_weight="1"
                    android:background="@drawable/btn_bg_red_15"
                    android:gravity="center"
                    android:text="全部关闭"
                    android:textColor="#ffffff"
                    android:textSize="14sp" />
    
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="50dp">
    
                <Button
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:visibility="invisible" />
            </LinearLayout>
    
    
        </LinearLayout>
    
    
    </ScrollView>


    展开全文
  • 添加校验和菜单本章将向你展示怎样为personForm添加校验逻辑,使得firstName和lastName在页面上变成必填field,并且添加一个屏幕列表来显示数据库中所有人的记录。这部分内容依赖于Part III: 创建Action和JSP关于本...

    添加校验和菜单

    本章将向你展示怎样为personForm添加校验逻辑,使得firstNamelastName在页面上变成必填field,并且添加一个屏幕列表来显示数据库中所有人的记录。

    这部分内容依赖于Part III: 创建ActionJSP

    关于本章

    本章将向你展示怎样使用StrutsValidator 为你的PersonForm对象添加校验逻辑(客户端和服务器端)。我们也会使用Display Tag Library 创建一个屏幕列表来显示数据库中所有的people

    内容

    ·   [1] Person.java 添加XDoclet Validator标签

    ·   [2] 检查和测试添加了校验的JSP

    ·   [3] DAOManager Test添加testGetPeople方法

    ·   [4] DAOManager添加getPeople()方法

    ·   [5] Action Test 添加testSearch() 方法

    ·   [6] Action添加search方法

    ·   [7] 创建personList.jspCanoo test

    ·   [8] 为菜单(Menu)添加链接(Link)

    Person.java 添加XDoclet Validator标签

    正常情况下,如果你想使用StrutsValidator,你必须用手写validation.xml文件。如果你不使用AppFuse 你也必须在ApplicationResources_en.properties文件中配置Validator Pluginerror key。如果你想获得更多的信息,请参见Validation Made Easy Tutorial

    我们要感谢Xdoclet,它使得这一切都变得很简单你只需要为Person类添加几个@struts.validator标签。打开它 (src/dao/**/model/Person.java),然后修改getFirstName()getLastName() 方法的JavaDoc,使它们包含 @struts.validator type="required" 标签。

        /**
         * @struts.validator type="required"
         * @hibernate.property column="first_name" length="50"
         */
        public String getFirstName() {
            return this.firstName;
        }

        /**
         * @struts.validator type="required" 
         * @hibernate.property column="last_name" length="50"
         */
        public String getLastName() {
            return this.lastName;
        }

    你还可以在这个标签里添加一个msgkey属性( attribute),它用来覆盖错误的默认消息的key

    @struts.validator type="required" msgkey="errors.required"

    type="required"的默认的Key已经是errors.required了,所以我经常保留它做为默认值。这个key被定义在web/WEB-INF/classes/ApplicationResources_*.properties中。 你也许注意到:虽然XDoclet的文档 说应该把这些标签放在Setter部分,但我们放到了getter部分,这是因为模板文件(metadata/template/struts_form.xdt) 会将这些标签放在产生的PersonForm.javaSetter部分。

    现在如果你保存Person.java,然后运行ant clean webdoclet,将会在目录build/你的项目名称/WEB-INF/下产生一个validation.xml文件. 它应该有一个为"personForm"使用的入口:

          <form name="personForm">
                  <field property="firstName"
                         depends="required">

                      <arg0 key="personForm.firstName"/>
                  </field>
                  <field property="lastName"
                         depends="required">

                      <arg0 key="personForm.lastName"/>
                  </field>
          </form>

    在我们的PersonForm.jsp中可以使用客户端校验,我们在PersonForm.jsp文件的底部添加一个<html:javascript>标签和一些脚本。下面的内容是已经存在的(感谢viewgen) – 你只需要将它们从注释中放开,它们被注释的原因是:如果指定了一个Form的名字并且校验规则不存在,Validator将会抛出一个异常。

    我个人认为这是一个 a bug ,Struts的提交者们并不这样认为.

    <html:javascript formName="personForm" cdata="false"

        dynamicJavascript="true" staticJavascript="false"/>

    <script type="text/javascript"

        src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

    现在你已经为你的Form配置了Validation,当这个Formaction-mapping中被配置后,并且设置了validate="true",这些校验规则将会生效。在上一章中, 我们为PersonAction添加"savePerson" action-mapping。这个action-mapping Xdoclet标签应该是这样的:

     * @struts.action name="personForm" path="/savePerson" scope="request"
     *  validate="true" parameter="method" input="edit"

    这样,只要web/pages/personForm.jsp<html:form action="savePerson">,当我们保存窗体时,校验将会生效。运行ant db-load deploy,启动Tomcat,然后浏览http://localhost:8080/appfuse/editPerson.html?id=1 。如果你删除了firstNamelastName field的值,然后单击save按钮,你应该会看到下面的JavaScript提示的警告:

    如果你确定一切都真正的如同期望的那样工作后,你就可以关掉JavaScript以保证可以使用服务器端的校验可以正常使用。在 Mozilla Firebird (我喜欢的浏览器)中,这些很容易,只是到Tools Options Web Features,然后去掉"Enable JavaScript"。现在如果你清除field的值,然后保存,你会看到如下的界面:

    如果你看不到上面的错误,那么可能你出现了下面的问题:

    ·   窗体显示保存成功,但firstNamelastName field是空的。

    这是因为web/pages/personForm.jsp 中的<html:form> action="editPerson"你要改为action="savePerson"

    ·   你单击了save,但出现了一个空白页面(blank page)

    你的"savePerson" forward"input" attribute配置不正确,一定要保证它指向了一个local或者global action-forward。在这个例子中,它应该是 input="edit"(指向.personDetail tile的定义)。根据我的经验,input的值必须是一个forward,不能是一个指向actionpath

    如果你只想使用服务器端校验(server-side validation(没有JavaScript)) ,你可以删除<html:form>onsubmit属性( web/pages/personForm.jsp) ,同时也要删除页面底部的Validator JavaScript标签:

    <html:javascript formName="personForm" cdata="false"
          dynamicJavascript="true" staticJavascript="false"/>
    <script type="text/javascript" 
          src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

    DAOManager Test添加testGetPeople方法

    为了创建一个List screen (也叫做master screen), 我们需要创建一个方法,这个方法将返回person 表中的所有记录,我们先为类PersonDAOTestPersonManagerTest添加测试方法。我通常将这个方法命名为getEntities (如: getUsers),但你也可以使用getAll或者search这样的名字 这只是个人喜好问题。

    打开test/dao/**/dao/PersonDAOTest.java然后添加testGetPeople方法:

        public void testGetPeople() {
            person = new Person();
            List results = dao.getPeople(person);
            assertTrue(results.size() > 0);
        }

    我把一个person对象传入getPeople的原因是将来可以使用person的某个()属性进行过滤。 是否在getPeople()方法中添加这个参数是可选的,但本篇文章在剩余部分都假设你已经这样做了。

    现在打开 test/service/**/service/PersonManagerTest.java,然后添加 testGetPeople 方法:

        public void testGetPeople() {
            List results = mgr.getPeople(new Person());
            assertTrue(results.size() > 0);
        }

    为了使这些测试类能够被编译通过, 你需要为类PersonDAO和接口PersonManager添加getPeople()方法,然后实现它们。

    DAOManager添加getPeople()方法

    打开src/dao/**/dao/PersonDAO.java,然后添加getPeople()方法声明(method signature):

        public List getPeople(Person person);

    现在为src/service/**/service/PersonManager.java添加同样的方法声明。保存你的文件,然后为你的测试文件添加必要的import。下一步我们需要在我们的实现类中实现getPeople() 方法。 打开 src/dao/**/dao/hibernate/PersonDAOHibernate.java,然后添加下面的方法:

        public List getPeople(Person person) {
            return getHibernateTemplate().find("from Person");
        }

    你可能注意到这里没有用person参数做任何事情. 现在它只是一个占位符将来你可能需要使用Hibernate's Query Language (HQL) or using Criteria Queries 来过滤它的属性值。

    下面是一个使用 Criteria Query的例子:

        Example example = Example.create(person)
                                 .excludeZeroes()    // exclude zero valued properties
                                 .ignoreCase();      // perform case insensitive string comparisons
        try {
            return getSession().createCriteria(Person.class)
                               .add(example)
                               .list();
        catch (Exception e) {
            throw new DataAccessException(e.getMessage());
        }
        return new ArrayList();

    现在我们在src/service/**/impl/PersonManagerImpl.java 中实现getPeople() 方法:

        public List getPeople(Person person) {
            return dao.getPeople(person);
        }

    保存你的修改,下面的命令应该都可以执行:

    ·   ant test-dao -Dtestcase=PersonDAO

    ·   ant test-service -Dtestcase=PersonManager

    如果一切都工作正常那真是漂亮的工作! 现在你需要为Web层添加这个retrieve all 功能。

    Action Test 添加testSearch() 方法

    打开test/web/**/action/PersonActionTest.java,然后添加如下方法:

        public void testSearch() {
            setRequestPathInfo("/editPerson");
            addRequestParameter("method""Search");
            actionPerform();

            verifyForward("list");

            assertNotNull(getRequest().getAttribute(Constants.PERSON_LIST));
            verifyNoActionErrors();
        }

    只有你在src/dao/**/Constants.java 文件中添加了PERSON_LIST变量后,这个类才能编译通过。

    我一般会复制一个已经存在的类似的变量如:USER_LIST.

        /**
         * The request scope attribute that holds the person list
         */
        public static final String PERSON_LIST = "personList";

    保存你修改的文件,因为PersonAction.search() 方法不存在,
    所以你还不能运行ant test-web -Dtestcase=PersonAction

    Action添加search方法

    打开src/web/**/action/PersonAction.java,然后在文件顶部添加Xdoclet标签- list screen添加forward

     * @struts.action-forward name="list" path="/WEB-INF/pages/PersonFormList.jsp"

    现在在PersonAction类的主题部分添加search方法。

    我使用UserAction.search()作为这个方法的模板。

        public ActionForward search(ActionMapping mapping, ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
                throws Exception {
            if (log.isDebugEnabled()) {
                log.debug("Entering 'search' method");
            }

            PersonManager mgr = (PersonManager) getBean("personManager");
            List people = mgr.getPeople(null);
            request.setAttribute(Constants.PERSON_LIST, people);

            // return a forward to the person list definition
            return mapping.findForward("list");
        }

    运行ant test-web -Dtestcase=PersonAction.

    多漂亮啊!

    BUILD SUCCESSFUL
    Total time: 1 minute 26 seconds

    创建personFormList.jspPersonListjspCanoo test

    在目录web/pages 下应该已经有一个PersonFormList.jsp(PersonList.Jsp)文件了,如果没有你可以使用viewgen创建它。使用命令行,进入到目录extras/viewgen下,运行ant -Dform.name=PersonForm。这将会在目录extras/viewgen/build下产生一个PersonFormList.jsp(PersonList.Jsp)文件。

    在目录web/pages下找到PersonFormList.jsp(PersonList.Jsp),打开。

    这个文件的头部是一个 <bean:struts>标签,它将edit screen forward作为一个page范围的变量。这个"editPerson"应该已经有一个值了。(This should already have a value of "editPerson")

    <%-- For linking to edit screen --%>
    <bean:struts id="editURL" forward="editPerson"/>

    将下面内容添加到metadata/web/global-forwards.xml,也是一个列表的视图。这样,它们就可以被包含到struts-config.xml文件中了。

    (Add this to the metadata/web/global-forwards.xml, as well as one for viewing the list. This way, they will get included in our struts-config.xml file.)

            <forward name="editPerson" path="/editPerson.html"/>
            <forward name="viewPeople" path="/editPerson.html?method=Search"/>

    你用来创建这个JSP的模板应该有一个对应硬编码的id属性的列。所以Xdoclet会把它添加2次。从PersonFormList.jsp(PersonList.Jsp)文件中删除下面的内容。

        <display:column property="id" sort="true" headerClass="sortable"
            titleKey="personForm.id"/>

    如果你知道更改 extras/viewgen/src/List_jsp.xdt文件能够不包含这个列标签的办法,请告诉我。

    你可能想去改变的另一件事情是:这个例子中产生的名字是"persons" ,它可能是people,在第31行附近,你应该可以看到下面的内容:

    <display:setProperty name="paging.banner.items_name" value="persons"/>

    更改为:

    <display:setProperty name="paging.banner.items_name" value="people"/>

    最后,在web/WEB-INF/classes/ApplicationResources_en.properties 文件中添加title headingkey (personList.titlepersonList.heading)。打开这个文件,然后添加下面的内容:

    # -- person list page --

    personList.title=Person List

    personList.heading=All People

    此时,你应该可以执行ant clean deploy,启动Tomcat,查看这个页面: http://localhost:8080/appfuse/editPerson.html?method=Search

    现在我们已经有一个List Screen 我们来修改这个添加或者删除了一个新Person后显示的页面。打开 src/web/**/action/PersonAction.java,在save, deletecancel方法中改变mapping.findForward("mainMenu")为下面这样:

        return mapping.findForward("viewPeople");

    你也需要改变test/web/**/action/PersonActionTest.javatestRemove方法的 verifyForward("mainMenu") verifyForward("viewPeople") 。最后,Canoo test "AddPerson""DeletePerson"需要被修改。打开test/web/web-tests.xml,然后在"AddPerson" target 部分更改如下内容:

    <verifytitle stepid="Main Menu appears if save successful"

        text="${webapp.prefix}${mainMenu.title}"/>

    改为:

    <verifytitle stepid="Person List appears if save successful"

        text="${webapp.prefix}${personList.title}"/>

    然后,在"DeletePerson" target部分,将下面的内容:

    <verifytitle stepid="display Main Menu"

        text="${webapp.prefix}$(mainMenu.title)"/>

    更改为:

    <verifytitle stepid="display Person List" text="${webapp.prefix}${personList.title}"/>

    我们使用"viewPeople"代替"list",这样search方法将会被执行。这比使用指向PersonForm.jsp的简单的list (它是一个forward)要好。

    我们要测试这个显示页是否工作,在test/web/web-tests.xml中创建一个新的JSP测试:

        <!-- Verify the people list screen displays without errors -->
        <target name="SearchPeople" 
            description="Tests search for and displaying all people">
            <canoo name="searchPeople">
                &config;
                <steps>
                    &login;
                    <invoke stepid="click View People link" url="/editPerson.html?method=Search"/>
                    <verifytitle stepid="we should see the personList title" 
                        text="${webapp.prefix}${personList.title}"/>
                </steps>
            </canoo>
        </target>

    我们在"PersonTests" target 中添加"SearchPeople" target,这样它将和其他与person相关的测试用例一起执行。

        <!-- runs person-related tests -->
        <target name="PersonTests" 
            depends="SearchPeople,EditPerson,SavePerson,AddPerson,DeletePerson"
            description="Call and executes all person test cases (targets)">
            <echo>Successfully ran all Person JSP tests!</echo>
        </target>

    现在我们可以运行ant test-canoo -Dtestcase=SearchPeople (或者:Tomcat没有运行时,我们执行ant test-jsp)


    为菜单(Menu)添加链接(Link)

    对于这个list的最后一步: 使用户可以看到add, editdelete功能。最简单的办法是在web/pages/mainMenu.jsp文件中添加一个链接的列表:

    NOTE: 不要使用mainMenu.jsp 中的其他的链接,这样可以保证JSP能够被其他的Web框架使用(如:Spring MVCWebWork)
    [The other links in mainMenu.jsp don't use so this JSP can be shared among the various web framework implementations in AppFuse (
    如:Spring MVC and WebWork).]

        <li>
            <html:link forward="viewPeople">
                <fmt:message key="menu.viewPeople"/>
            </html:link>
        </li>

    web/WEB-INF/classes/ApplicationResources_en.properties 中有一个条目: menu.viewPeople

    menu.viewPeople=View People

    另一种办法是:添加下面的内容到web/WEB-INF/menu-config.xml:

    [The other (more likely) alternative is that you'll want to add it to the menu. To do this, add the following to web/WEB-INF/menu-config.xml:]

    <Menu name="PeopleMenu" title="menu.viewPeople" forward="viewPeople"/>

    要保证<Menus> tag中有上面的XML代码,而不是在另一个<Menu>中。 然后添加这个心菜单到web/pages/menu.jsp现在它看起来应该是这样的:

    <%@ include file="/common/taglibs.jsp"%>

    <div id="menu">
    <menu:useMenuDisplayer name="ListMenu" permissions="rolesAdapter">
        <menu:displayMenu name="AdminMenu"/>
        <menu:displayMenu name="UserMenu"/>
        <menu:displayMenu name="PeopleMenu"/>
        <menu:displayMenu name="FileUpload"/>
        <menu:displayMenu name="FlushCache"/>
        <menu:displayMenu name="Clickstream"/>
    </menu:useMenuDisplayer>
    </div>

    现在你运行ant clean deploy,启动Tomcat,然后查看 http://localhost:8080/appfuse/mainMenu.html ,你应该可以看到下面的内容:

    需要注意的是左边多了一个新的链接,它从mainMenu.jsp中获取,在右边的菜单中也多了一个新的链接,它从menu.jsp中获取。

    恭喜你,至此,你已经完成了全部的使用AppFuse进行开发的流程!如果你成功的运行了上面的所有的测试用例,那我们现在开始真正的测试。停止Tomcat,运行ant clean test-all. 这将运行所有的工程中的单元测试. 提示:使用ant setup-db setup-tomcat test-all 可以很容易的对我们的系统雏形进行测试。另外,如果你正在寻找稳定性和健壮性的例子 - checkout Struts Resume.

    多好的一天!

    BUILD SUCCESSFUL
    Total time: 2 minutes 31 seconds

    如果你喜欢,你可以在这里下载我们这个指南中创建的文件。







    2004
    1110日星期三


    展开全文
  • 添加校验和菜单本章将向你展示怎样为personForm添加校验逻辑,使得firstName和lastName在页面上变成必填field,并且添加一个屏幕列表来显示数据库中所有人的记录。这部分内容依赖于Part III: 创建Action和JSP关于本...

    添加校验和菜单

    本章将向你展示怎样为personForm添加校验逻辑,使得firstNamelastName在页面上变成必填field,并且添加一个屏幕列表来显示数据库中所有人的记录。

    这部分内容依赖于Part III: 创建ActionJSP

    关于本章

    本章将向你展示怎样使用StrutsValidator 为你的PersonForm对象添加校验逻辑(客户端和服务器端)。我们也会使用Display Tag Library 创建一个屏幕列表来显示数据库中所有的people

    内容

    ·   [1] Person.java 添加XDoclet Validator标签

    ·   [2] 检查和测试添加了校验的JSP

    ·   [3] DAOManager Test添加testGetPeople方法

    ·   [4] DAOManager添加getPeople()方法

    ·   [5] Action Test 添加testSearch() 方法

    ·   [6] Action添加search方法

    ·   [7] 创建personList.jspCanoo test

    ·   [8] 为菜单(Menu)添加链接(Link)

    Person.java 添加XDoclet Validator标签

    正常情况下,如果你想使用StrutsValidator,你必须用手写validation.xml文件。如果你不使用AppFuse 你也必须在ApplicationResources_en.properties文件中配置Validator Pluginerror key。如果你想获得更多的信息,请参见Validation Made Easy Tutorial

    我们要感谢Xdoclet,它使得这一切都变得很简单你只需要为Person类添加几个@struts.validator标签。打开它 (src/dao/**/model/Person.java),然后修改getFirstName()getLastName() 方法的JavaDoc,使它们包含 @struts.validator type="required" 标签。

        /**
         * @struts.validator type="required"
         * @hibernate.property column="first_name" length="50"
         */
        public String getFirstName() {
            return this.firstName;
        }

        /**
         * @struts.validator type="required" 
         * @hibernate.property column="last_name" length="50"
         */
        public String getLastName() {
            return this.lastName;
        }

    你还可以在这个标签里添加一个msgkey属性( attribute),它用来覆盖错误的默认消息的key

    @struts.validator type="required" msgkey="errors.required"

    type="required"的默认的Key已经是errors.required了,所以我经常保留它做为默认值。这个key被定义在web/WEB-INF/classes/ApplicationResources_*.properties中。 你也许注意到:虽然XDoclet的文档 说应该把这些标签放在Setter部分,但我们放到了getter部分,这是因为模板文件(metadata/template/struts_form.xdt) 会将这些标签放在产生的PersonForm.javaSetter部分。

    现在如果你保存Person.java,然后运行ant clean webdoclet,将会在目录build/你的项目名称/WEB-INF/下产生一个validation.xml文件. 它应该有一个为"personForm"使用的入口:

          <form name="personForm">
                  <field property="firstName"
                         depends="required">

                      <arg0 key="personForm.firstName"/>
                  </field>
                  <field property="lastName"
                         depends="required">

                      <arg0 key="personForm.lastName"/>
                  </field>
          </form>

    在我们的PersonForm.jsp中可以使用客户端校验,我们在PersonForm.jsp文件的底部添加一个<html:javascript>标签和一些脚本。下面的内容是已经存在的(感谢viewgen) – 你只需要将它们从注释中放开,它们被注释的原因是:如果指定了一个Form的名字并且校验规则不存在,Validator将会抛出一个异常。

    我个人认为这是一个 a bug ,Struts的提交者们并不这样认为.

    <html:javascript formName="personForm" cdata="false"

        dynamicJavascript="true" staticJavascript="false"/>

    <script type="text/javascript"

        src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

    现在你已经为你的Form配置了Validation,当这个Formaction-mapping中被配置后,并且设置了validate="true",这些校验规则将会生效。在上一章中, 我们为PersonAction添加"savePerson" action-mapping。这个action-mapping Xdoclet标签应该是这样的:

     * @struts.action name="personForm" path="/savePerson" scope="request"
     *  validate="true" parameter="method" input="edit"

    这样,只要web/pages/personForm.jsp<html:form action="savePerson">,当我们保存窗体时,校验将会生效。运行ant db-load deploy,启动Tomcat,然后浏览http://localhost:8080/appfuse/editPerson.html?id=1 。如果你删除了firstNamelastName field的值,然后单击save按钮,你应该会看到下面的JavaScript提示的警告:

    如果你确定一切都真正的如同期望的那样工作后,你就可以关掉JavaScript以保证可以使用服务器端的校验可以正常使用。在 Mozilla Firebird (我喜欢的浏览器)中,这些很容易,只是到Tools Options Web Features,然后去掉"Enable JavaScript"。现在如果你清除field的值,然后保存,你会看到如下的界面:

    如果你看不到上面的错误,那么可能你出现了下面的问题:

    ·   窗体显示保存成功,但firstNamelastName field是空的。

    这是因为web/pages/personForm.jsp 中的<html:form> action="editPerson"你要改为action="savePerson"

    ·   你单击了save,但出现了一个空白页面(blank page)

    你的"savePerson" forward"input" attribute配置不正确,一定要保证它指向了一个local或者global action-forward。在这个例子中,它应该是 input="edit"(指向.personDetail tile的定义)。根据我的经验,input的值必须是一个forward,不能是一个指向actionpath

    如果你只想使用服务器端校验(server-side validation(没有JavaScript)) ,你可以删除<html:form>onsubmit属性( web/pages/personForm.jsp) ,同时也要删除页面底部的Validator JavaScript标签:

    <html:javascript formName="personForm" cdata="false"
          dynamicJavascript="true" staticJavascript="false"/>
    <script type="text/javascript" 
          src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

    DAOManager Test添加testGetPeople方法

    为了创建一个List screen (也叫做master screen), 我们需要创建一个方法,这个方法将返回person 表中的所有记录,我们先为类PersonDAOTestPersonManagerTest添加测试方法。我通常将这个方法命名为getEntities (如: getUsers),但你也可以使用getAll或者search这样的名字 这只是个人喜好问题。

    打开test/dao/**/dao/PersonDAOTest.java然后添加testGetPeople方法:

        public void testGetPeople() {
            person = new Person();
            List results = dao.getPeople(person);
            assertTrue(results.size() > 0);
        }

    我把一个person对象传入getPeople的原因是将来可以使用person的某个()属性进行过滤。 是否在getPeople()方法中添加这个参数是可选的,但本篇文章在剩余部分都假设你已经这样做了。

    现在打开 test/service/**/service/PersonManagerTest.java,然后添加 testGetPeople 方法:

        public void testGetPeople() {
            List results = mgr.getPeople(new Person());
            assertTrue(results.size() > 0);
        }

    为了使这些测试类能够被编译通过, 你需要为类PersonDAO和接口PersonManager添加getPeople()方法,然后实现它们。

    DAOManager添加getPeople()方法

    打开src/dao/**/dao/PersonDAO.java,然后添加getPeople()方法声明(method signature):

        public List getPeople(Person person);

    现在为src/service/**/service/PersonManager.java添加同样的方法声明。保存你的文件,然后为你的测试文件添加必要的import。下一步我们需要在我们的实现类中实现getPeople() 方法。 打开 src/dao/**/dao/hibernate/PersonDAOHibernate.java,然后添加下面的方法:

        public List getPeople(Person person) {
            return getHibernateTemplate().find("from Person");
        }

    你可能注意到这里没有用person参数做任何事情. 现在它只是一个占位符将来你可能需要使用Hibernate's Query Language (HQL) or using Criteria Queries 来过滤它的属性值。

    下面是一个使用 Criteria Query的例子:

        Example example = Example.create(person)
                                 .excludeZeroes()    // exclude zero valued properties
                                 .ignoreCase();      // perform case insensitive string comparisons
        try {
            return getSession().createCriteria(Person.class)
                               .add(example)
                               .list();
        catch (Exception e) {
            throw new DataAccessException(e.getMessage());
        }
        return new ArrayList();

    现在我们在src/service/**/impl/PersonManagerImpl.java 中实现getPeople() 方法:

        public List getPeople(Person person) {
            return dao.getPeople(person);
        }

    保存你的修改,下面的命令应该都可以执行:

    ·   ant test-dao -Dtestcase=PersonDAO

    ·   ant test-service -Dtestcase=PersonManager

    如果一切都工作正常那真是漂亮的工作! 现在你需要为Web层添加这个retrieve all 功能。

    Action Test 添加testSearch() 方法

    打开test/web/**/action/PersonActionTest.java,然后添加如下方法:

        public void testSearch() {
            setRequestPathInfo("/editPerson");
            addRequestParameter("method""Search");
            actionPerform();

            verifyForward("list");

            assertNotNull(getRequest().getAttribute(Constants.PERSON_LIST));
            verifyNoActionErrors();
        }

    只有你在src/dao/**/Constants.java 文件中添加了PERSON_LIST变量后,这个类才能编译通过。

    我一般会复制一个已经存在的类似的变量如:USER_LIST.

        /**
         * The request scope attribute that holds the person list
         */
        public static final String PERSON_LIST = "personList";

    保存你修改的文件,因为PersonAction.search() 方法不存在,
    所以你还不能运行ant test-web -Dtestcase=PersonAction

    Action添加search方法

    打开src/web/**/action/PersonAction.java,然后在文件顶部添加Xdoclet标签- list screen添加forward

     * @struts.action-forward name="list" path="/WEB-INF/pages/PersonFormList.jsp"

    现在在PersonAction类的主题部分添加search方法。

    我使用UserAction.search()作为这个方法的模板。

        public ActionForward search(ActionMapping mapping, ActionForm form,
                                    HttpServletRequest request,
                                    HttpServletResponse response)
                throws Exception {
            if (log.isDebugEnabled()) {
                log.debug("Entering 'search' method");
            }

            PersonManager mgr = (PersonManager) getBean("personManager");
            List people = mgr.getPeople(null);
            request.setAttribute(Constants.PERSON_LIST, people);

            // return a forward to the person list definition
            return mapping.findForward("list");
        }

    运行ant test-web -Dtestcase=PersonAction.

    多漂亮啊!

    BUILD SUCCESSFUL
    Total time: 1 minute 26 seconds

    创建personFormList.jspPersonListjspCanoo test

    在目录web/pages 下应该已经有一个PersonFormList.jsp(PersonList.Jsp)文件了,如果没有你可以使用viewgen创建它。使用命令行,进入到目录extras/viewgen下,运行ant -Dform.name=PersonForm。这将会在目录extras/viewgen/build下产生一个PersonFormList.jsp(PersonList.Jsp)文件。

    在目录web/pages下找到PersonFormList.jsp(PersonList.Jsp),打开。

    这个文件的头部是一个 <bean:struts>标签,它将edit screen forward作为一个page范围的变量。这个"editPerson"应该已经有一个值了。(This should already have a value of "editPerson")

    <%-- For linking to edit screen --%>
    <bean:struts id="editURL" forward="editPerson"/>

    将下面内容添加到metadata/web/global-forwards.xml,也是一个列表的视图。这样,它们就可以被包含到struts-config.xml文件中了。

    (Add this to the metadata/web/global-forwards.xml, as well as one for viewing the list. This way, they will get included in our struts-config.xml file.)

            <forward name="editPerson" path="/editPerson.html"/>
            <forward name="viewPeople" path="/editPerson.html?method=Search"/>

    你用来创建这个JSP的模板应该有一个对应硬编码的id属性的列。所以Xdoclet会把它添加2次。从PersonFormList.jsp(PersonList.Jsp)文件中删除下面的内容。

        <display:column property="id" sort="true" headerClass="sortable"
            titleKey="personForm.id"/>

    如果你知道更改 extras/viewgen/src/List_jsp.xdt文件能够不包含这个列标签的办法,请告诉我。

    你可能想去改变的另一件事情是:这个例子中产生的名字是"persons" ,它可能是people,在第31行附近,你应该可以看到下面的内容:

    <display:setProperty name="paging.banner.items_name" value="persons"/>

    更改为:

    <display:setProperty name="paging.banner.items_name" value="people"/>

    最后,在web/WEB-INF/classes/ApplicationResources_en.properties 文件中添加title headingkey (personList.titlepersonList.heading)。打开这个文件,然后添加下面的内容:

    # -- person list page --

    personList.title=Person List

    personList.heading=All People

    此时,你应该可以执行ant clean deploy,启动Tomcat,查看这个页面: http://localhost:8080/appfuse/editPerson.html?method=Search

    现在我们已经有一个List Screen 我们来修改这个添加或者删除了一个新Person后显示的页面。打开 src/web/**/action/PersonAction.java,在save, deletecancel方法中改变mapping.findForward("mainMenu")为下面这样:

        return mapping.findForward("viewPeople");

    你也需要改变test/web/**/action/PersonActionTest.javatestRemove方法的 verifyForward("mainMenu") verifyForward("viewPeople") 。最后,Canoo test "AddPerson""DeletePerson"需要被修改。打开test/web/web-tests.xml,然后在"AddPerson" target 部分更改如下内容:

    <verifytitle stepid="Main Menu appears if save successful"

        text="${webapp.prefix}${mainMenu.title}"/>

    改为:

    <verifytitle stepid="Person List appears if save successful"

        text="${webapp.prefix}${personList.title}"/>

    然后,在"DeletePerson" target部分,将下面的内容:

    <verifytitle stepid="display Main Menu"

        text="${webapp.prefix}$(mainMenu.title)"/>

    更改为:

    <verifytitle stepid="display Person List" text="${webapp.prefix}${personList.title}"/>

    我们使用"viewPeople"代替"list",这样search方法将会被执行。这比使用指向PersonForm.jsp的简单的list (它是一个forward)要好。

    我们要测试这个显示页是否工作,在test/web/web-tests.xml中创建一个新的JSP测试:

        <!-- Verify the people list screen displays without errors -->
        <target name="SearchPeople" 
            description="Tests search for and displaying all people">
            <canoo name="searchPeople">
                &config;
                <steps>
                    &login;
                    <invoke stepid="click View People link" url="/editPerson.html?method=Search"/>
                    <verifytitle stepid="we should see the personList title" 
                        text="${webapp.prefix}${personList.title}"/>
                </steps>
            </canoo>
        </target>

    我们在"PersonTests" target 中添加"SearchPeople" target,这样它将和其他与person相关的测试用例一起执行。

        <!-- runs person-related tests -->
        <target name="PersonTests" 
            depends="SearchPeople,EditPerson,SavePerson,AddPerson,DeletePerson"
            description="Call and executes all person test cases (targets)">
            <echo>Successfully ran all Person JSP tests!</echo>
        </target>

    现在我们可以运行ant test-canoo -Dtestcase=SearchPeople (或者:Tomcat没有运行时,我们执行ant test-jsp)


    为菜单(Menu)添加链接(Link)

    对于这个list的最后一步: 使用户可以看到add, editdelete功能。最简单的办法是在web/pages/mainMenu.jsp文件中添加一个链接的列表:

    NOTE: 不要使用mainMenu.jsp 中的其他的链接,这样可以保证JSP能够被其他的Web框架使用(如:Spring MVCWebWork)
    [The other links in mainMenu.jsp don't use so this JSP can be shared among the various web framework implementations in AppFuse (
    如:Spring MVC and WebWork).]

        <li>
            <html:link forward="viewPeople">
                <fmt:message key="menu.viewPeople"/>
            </html:link>
        </li>

    web/WEB-INF/classes/ApplicationResources_en.properties 中有一个条目: menu.viewPeople

    menu.viewPeople=View People

    另一种办法是:添加下面的内容到web/WEB-INF/menu-config.xml:

    [The other (more likely) alternative is that you'll want to add it to the menu. To do this, add the following to web/WEB-INF/menu-config.xml:]

    <Menu name="PeopleMenu" title="menu.viewPeople" forward="viewPeople"/>

    要保证<Menus> tag中有上面的XML代码,而不是在另一个<Menu>中。 然后添加这个心菜单到web/pages/menu.jsp现在它看起来应该是这样的:

    <%@ include file="/common/taglibs.jsp"%>

    <div id="menu">
    <menu:useMenuDisplayer name="ListMenu" permissions="rolesAdapter">
        <menu:displayMenu name="AdminMenu"/>
        <menu:displayMenu name="UserMenu"/>
        <menu:displayMenu name="PeopleMenu"/>
        <menu:displayMenu name="FileUpload"/>
        <menu:displayMenu name="FlushCache"/>
        <menu:displayMenu name="Clickstream"/>
    </menu:useMenuDisplayer>
    </div>

    现在你运行ant clean deploy,启动Tomcat,然后查看 http://localhost:8080/appfuse/mainMenu.html ,你应该可以看到下面的内容:

    需要注意的是左边多了一个新的链接,它从mainMenu.jsp中获取,在右边的菜单中也多了一个新的链接,它从menu.jsp中获取。

    恭喜你,至此,你已经完成了全部的使用AppFuse进行开发的流程!如果你成功的运行了上面的所有的测试用例,那我们现在开始真正的测试。停止Tomcat,运行ant clean test-all. 这将运行所有的工程中的单元测试. 提示:使用ant setup-db setup-tomcat test-all 可以很容易的对我们的系统雏形进行测试。另外,如果你正在寻找稳定性和健壮性的例子 - checkout Struts Resume.

    多好的一天!

    BUILD SUCCESSFUL
    Total time: 2 minutes 31 seconds

    如果你喜欢,你可以在这里下载我们这个指南中创建的文件。

    展开全文
  • CSS基础

    2020-07-14 22:04:40
    这里写目录标题1、CSS是用来做什么的?2、CSS的写法3、css与html的链接4、CSS规则集详解4.1、多元素选择4.2 、不同类型的选择器4.3、字体和文本4.4、一切皆盒子4.5、更改页面颜色...怎样将内容显示在屏幕的特定位置?
  • 点击我的豆瓣 在右下方的 添加我的豆瓣收藏秀 进去后设置,然后根据指导把代码复制到自己的页面中即可 以后评价过的书都会在这个页面显示的 阅读是一个非常好的生活习惯,往往书本中的铅字比屏幕上的字更有深度 ...
  • Div+CSS 布局大全

    2018-01-06 00:21:20
    然后再针对语义、结构添加 CSS。这篇文章将告诉你应该怎样把 HTML 结构化。 另一种原因是你对那些非常熟悉的表现层属性(例如:cellpadding,、hspace、align="left"等等)束手无策,不知道该 转换成对应的什么 CSS ...
  • 2.为按钮添加对话框 3.删除表格选定记录 4.删除表格记录警告 5.点击表格行链接另一页 6.表格超连接列传递参数 7.表格点击改变颜色 8.关于日期格式 9.获取错误信息并到指定页面 10.清空Cookie 11.自定义异常...
  • 如何确定当前打印机的页面大小和方向 如何确定打印队列的状态 第11章 操作系统 如何获得Windows的版本号 如何得到当前Windows是不是安全模式 如何查询注册表中的键 如何修改注册表中的键 如何在注册表中增加键 ...
  • dreamweaver上PHP网站开发相关插件

    热门讨论 2009-09-01 14:40:19
    定位层的横向流动,不管浏览器横向大小怎样变化,该层均不会改变位置。 Popup Message.mxp 在“提交”按钮上添加注释。 Compare Fields.mxp 对比两个表单的内容是否相同,多用于密码验证。 Fade Link.mxp 使...
  • PHP动态网站开发插件

    2013-06-18 22:47:24
    定位层的横向流动,不管浏览器横向大小怎样变化,该层均不会改变位置。 Popup Message.mxp Download 点击下载 在“提交”按钮上添加注释。 Compare Fields.mxp Download 点击下载 对比两个表单的内容是否相同,多...
  • 怎样写一个存储过程类来实现如何对存储过程的调用?.txt 直接拉入图片文件到程序窗口中打开.txt 指定Web服务器运行的不是ASP.NET1.1版.txt 字符串加密.txt 字幕显示屏幕保护.txt 自己动手用c#写控件.txt C#...
  • 怎样写一个存储过程类来实现如何对存储过程的调用?.txt 直接拉入图片文件到程序窗口中打开.txt 指定Web服务器运行的不是ASP.NET1.1版.txt 字符串加密.txt 字幕显示屏幕保护.txt 自己动手用c#写控件.txt C#...
  • 11.5 在一个页面上建立照片的多个副本 11.6 怎样通过电子邮件传递照片 11.7 艺术海报版面设计 11.8 简单的3幅照片平衡版面 11.9 从一幅照片创建多幅照片效果 11.10 即时全景画版面设计 第十三章 我的工作流 11.1 我...
  • http:\\www.grasp.com.cn)去下载最新的版本,然后对你的计算机里的老版本《管家婆》做一个升级安装即可(关于如何“解压下载后的文件”及“升级安装的方法”可以参看主页上“软件下载”页面上的说明,如果还有问题...
  • C#开发经验技巧宝典

    2008-10-14 20:28:23
    1107 将“屏幕保护”选项删除 674 1108 设置“任务栏”中时间显示样式 674 25.2 开始菜单与其他设置 674 1109 设定文档记录的个数的程序 674 1110 禁用“开始”菜单中的“运行”功能的程序 675 1111 启用...
  • 多媒体教室

    2013-06-14 08:10:31
    此模式下,屏幕广播的内容为1:1显示,窗口显示的内容能根据屏幕广播或录制时教师的鼠标移动情况而做相应滚动,这样就不会出现在自由滚动模式下有时会看不到教师的操作以及在缩放显示模式下看不清楚屏幕广播具体...
  • 14.8.2 添加自动化信息 322 14.8.3 上传和其他报告 326 14.8.4 日志和监控服务 327 14.9 没有万无一失的系统 328 14.10 小结 328 第15 章 结论 330 15.1 数据处理者的职责 330 15.2...
  • 4.2.4 怎样操作RichtextBox控件的选择文本 4.2.5 如何获取数据表中字段的描述信息 4.3 设计过程 4.3.1 获取数据表中字段的中文信息 4.3.2 添加数据表的查询条件 4.3.3 向SQL语句中添加括号 4.3.4 查询生成后的SQL...
  • 4.2.4 怎样操作RichtextBox控件的选择文本 4.2.5 如何获取数据表中字段的描述信息 4.3 设计过程 4.3.1 获取数据表中字段的中文信息 4.3.2 添加数据表的查询条件 4.3.3 向SQL语句中添加括号 4.3.4 查询生成后的SQL...
  • 4.2.4 怎样操作RichtextBox控件的选择文本 4.2.5 如何获取数据表中字段的描述信息 4.3 设计过程 4.3.1 获取数据表中字段的中文信息 4.3.2 添加数据表的查询条件 4.3.3 向SQL语句中添加括号 4.3.4 查询生成后的SQL...
  • Nehe的OpenGL教程电子书

    2018-04-07 12:25:03
    代码仍然使用第一章的,但是,课程页面只是解释了新的部分,包括导入3D场景,在3D世界中移动。下载VC++代码并且在你阅读教程的同时阅读代码。按[B]键控制混合,[F]键控制滤波,[L]键控制光照(但光并不随场景移动)...
  • C#开发典型模块大全

    2014-03-12 18:11:22
    4.2.4 怎样操作RichtextBox控件的选择文本 82 4.2.5 如何获取数据表中字段的描述信息 83 4.3 设计过程 83 4.3.1 获取数据表中字段的中文信息 84 4.3.2 添加数据表的查询条件 86 4.3.3 向SQL语句中添加...

空空如也

空空如也

1 2 3
收藏数 54
精华内容 21
关键字:

怎样添加屏幕页面