20 septembre 2011

Mobile avec Sencha Touch

Dans l'article précédent, je présente le cadre d'évaluation de plusieurs frameworks pour le développement d'application mobile. Cet article présente le détail pour Sencha Touch.

Sencha Touch est le plus évolué des frameworks que nous avons évalué. La qualité de finition des composants et le support avancé des gestes propres à un écran tactile en font un framework haut de gamme qui supporte des ambitions élevées. Cependant, cette richesse vient au prix d'une courbe d'apprentissage beaucoup plus importante, typique du développement d'application "desktop", plus que du web. Alors que les autres frameworks évalués capitalisaient sur une fondation HTML, Sencha touch est basé exclusivement sur du javascript. En effet, dans la page HTML de l'application, on ne trouve absolument rien dans le BODY, tout le DOM est créé dynamiquement au chargement de la page.

Contrairement aux autres, qui se limitent à l'interface, Sencha touch offre plus de support pour la portion de gestion des données.

Bien qu'il soit relativement facile de monter une petite application à partir des exemples et de la documentation très complète, la réalisation se heurte parfois à des écueils difficiles à passer. Peut-être la version 2 qui vient d'être publiée facilitera le travail.

A plus de 350 kb, Sencha touch à un chargement initial assez long, mais il répond très bien par la suite.

 arbrSenchaListe.png

 

arbrSenchaDetail.png

Voici le code pour l'application avec Sencha Touch:

 

<!DOCTYPE html>
<!-- Sencha touch ... -->
<html>
<head>
<title>Demo Sencha touch</title>
<%--
<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8">
<metacharset="utf-8"/>
<metaname="viewport"
    content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<metaname="apple-touch-fullscreen"content="yes">
<metaname="apple-mobile-web-app-capable"content="yes"><!--this is buggy??!!-->
<metaname="apple-mobile-web-app-status-bar-style"content="black">
<linkrel="apple-touch-startup-image"href="../images/splash.png"/>
<linkrel="apple-touch-icon"href="../images/logo.png">
<linkrel="icon"type="image/ico"href="../images/logo.png">
--%>
<linkrel="stylesheet"
    href="${resource(dir:'css',file:'sencha-touch.css')}"/>
<scripttype="text/javascript"
    src="${resource(dir:'js',file:'sencha-touch.js')}">
    </script>
<script  type="text/javascript">
Ext.setup({
    tabletStartupScreen:'tablet_startup.png',
    phoneStartupScreen:'phone_startup.png',
    icon:'icon.png',
    glossOnIcon:false,        
 
    onReady:function(){
        Ext.regModel('Arbre',{
            fields:['nomLatin','nomFrancais','vignette','image','type','distance','distanceNumerique']
        });
        
        
        var store =new Ext.data.Store({
            model:'Arbre',
            sorters:'nomLatin',
            getGroupString :function(record){
                return record.get('nomLatin')[0];
            },
            data:[
                   <g:each in="${arbreInstanceList}" status="i"var="arbreInstance">
                     { nomLatin:"${fieldValue(bean: arbreInstance, field: "nomLatin")}",
                       nomFrancais:" ${fieldValue(bean: arbreInstance, field:"nomFrancais")}",
                       image:" ${fieldValue(bean: arbreInstance, field: "imageURL")}",
                       type:" ${fieldValue(bean: arbreInstance, field: "type")}",
                       distance:" ${fieldValue(bean: arbreInstance, field:"distanceMinimale")}",
                       distanceNumerique:" ${fieldValue(bean: arbreInstance, field:"distanceAsInteger")}",
                       vignette:" ${fieldValue(bean: arbreInstance, field:"vignetteURL")}"},
                  </g:each>                 
              ]
        });
        
 var sliderDistance =new Ext.form.Slider({
     width:300,
     value:14.0,
    increment:1.0,
    minValue:0,
    maxValue:15.0,
    label:'Distance',
    listeners:{
         change:function(slider, thumb,newValue){
              this.label ='changed!';
     
             console.log('slider changed to : '+newValue);
             store.filterBy(function(record,id){
                 var b =(record.data.distanceNumerique <= newValue);
                 return b;
                 })
         }
        }
});
 
 
 
        var detailToolbar =new Ext.Toolbar({
            height:66,
            items:[{
                text:'Liste',
                ui:'back',
                handler:function(){
                    corePanel.setActiveItem('listwrapper',{type:'slide', direction:'right'});
                }
              },
        
            {
                xtype:'spacer'   
            },
                sliderDistance
            ,
            {
                xtype:'checkboxfield',
                label:'Feuillus',
                width:150,
                listeners:{
                    check:function(self){
                        store.filter('type',' Feuillu');
                    },
                    uncheck:function(self){
                        store.clearFilter(false);
                    }
                }
                 },
            {
                xtype    :'textfield',
                name     :'field1',
                emptyText:'enter search term',
                listeners:{
                     keyUp:function(self, e){
                         console.log('field changed to : '+this.getValue());
                         var arr =new Array();
                         arr = document.getElementsByClassName('x-list-item');
                         for(var i =0; i < arr.length; i++){
                             var obj = document.getElementsByClassName('x-list-item').item(i);
                             if(obj.textContent
                                     .indexOf(this.getValue())==-1){
                                 obj.style.display ="none";
                             }else{
                                 obj.style.display ="block";
                             }
                         }
                     }
                    }
            }
            ]
        });
        
         var detailPanel =new Ext.Panel({
            id:'detailpanel',
            tpl:'<div class=detail>'+
                '<img src="{image}"/>'+
                '<p class=distance>{distance}</p>'+
                '<p>{type}</p>'+
                '<p class="lt">{nomLatin}</p>'+
                '<p>{nomFrancais}</p>'+
                '  </div>',
            dockedItems:[detailToolbar],
 
            afterRender:function(){
                this.mon(this.el,{
                    swipe:this.handleEvent,
                    
                });
            },
                    
            handleEvent:function(e){
                if(e.direction=='right'){
                    corePanel.setActiveItem('listwrapper',{type:'slide', direction:'right'});}
            }
 
        });
        
        var nestedList =new Ext.List({
             id:'indexlist',
            fullscreen:true,
            title:'Arbres',
            itemTpl:'<div class="arbre"><img class=inList src="{vignette}" /><p class="lt"> {nomLatin} </p><p class="fr">{nomFrancais}</p><p class="dist">{distance}</p></div>',
            grouped:true,
            indexBar:true,
            dock:'top',
            store: store,
            listeners:{
                  itemtap:function(record, index,item, e){
                      detailToolbar.setTitle(record.getStore().getAt(index).data.nomLatin);      
                    detailPanel.update(record.getStore().getAt(index).data);
                    corePanel.setActiveItem('detailpanel');
                    }
       },
            onItemDisclosure:function(record, btn, index){
                detailPanel.update(record.data);
                corePanel.setActiveItem('detailpanel');
                
            }
        });
 
        var listWrapper =new Ext.Panel({
            id:'listwrapper',
            layout:'fit',
            items:[nestedList],
            dockedItems:[
               detailToolbar
            ]
        });
 
        var btnSpecTop =[
<%--            { ui:'back', text:'Back'},--%>
<%--            { xtype:'spacer'},--%>
<%--            { ui:'default', text:'Login'}--%>
        ]// end btnSpecTop
 
 
        var tapHandler =function(btn, evt){
            alert("Button '"+ btn.text +"' tapped.");
        }        
 
        var windowHauteur = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
 
        var corePanel  =new Ext.Panel ({
            fullscreen:true,
            layout:'card',
            cardSwitchAnimation:'slide',
            items:[listWrapper,detailPanel]
            
        });
        
        var dockedItems =[{
            xtype:'toolbar',
            dock:'top',
            title:'Arbr',
            items: btnSpecTop,
            defaults:{ handler: tapHandler }
            },
            {
                xtype:'tabpanel',
               height: windowHauteur  -48,
                defaults:{
                    scroll:'vertical'
                },
               
                tabBar:{
                    dock:'bottom',
                    ui:'dark',
                    layout:{
                        pack:'center'
                    }
                },
                items:[{
                    title:'Liste',
                    iconCls:'home',
                    cls:'card card1',
                    badgeText:'',
                    items:[corePanel]
              
                },
                {
                    title:'Zone',
                    html:'<img src="http://www.hydroquebec.com/arbres/images/img_zones_rusticite.gif" class="fitToPort"></img>',
                    iconCls:'locate',
                    cls:'card card3',
                    
                },
                {
                    title:'Préférences',
                    html:'<p>test 3</p>',
                    iconCls:'settings',
                    cls:'card card3'
                }]
             
            }                
        ]
 
        var appPanel =new Ext.Panel({
            id:'appPanel',
            fullscreen:true,
            dockedItems: dockedItems            
        });
        
        
    }   // end onReady 
    });
</script>
<style>
.card3 {
    background-color:rgb(220,221,223);
    text-align:center;
    }
.fitToPort {
width:100%;}
.detail {
margin:1em;}
</style>
</head>
<body>
 
 
</body>
</html>

Mobile avec jQuery mobile

Dans l'article précédent, je présente le cadre d'évaluation de plusieurs frameworks pour le développement d'application mobile. Cet article présente le détail pour jQuery Mobile.

La particularité de jQuery mobile est son support pour un grand, très grand, nombre d'appareils mobiles. La mise en œuvre est relativement facile et très axée sur le code HTML avec l'utilisation d'attributs "data-quelqueChose". La variété de composants d'interface est assez riche et offre une bonne base. Les rendus sont de qualité, comme le montre la figure suivante. La documentation est correcte, sans plus et la qualité générale est au-dessus de la moyenne.

La version testée, 1.0b1, se traîne souvent les pieds, autant au chargement que dans les effets de transition. Espérons que les nouvelles versions amélioreront cet aspect.

Pour un framework de base fonctionnant sur plusieurs appareils c'est un excellent choix.

 

arbrJQmobileListe.png

arbrJQMobileDetail.png

Voici le code pour jQuery Mobile

 

<!DOCTYPE html>
<html>
<head>
<title>Demo JQMobile</title>
 
<linkrel="stylesheet"
    href="${resource(dir:'js/jquery.mobile-1.0b1',file:'jquery.mobile-1.0b1.min.css')}"/>
<scripttype="text/javascript"
    src="${resource(dir:'js/jquery.mobile-1.0b1',file:'jquery-1.6.1.min.js')}">
    </script>
<scripttype="text/javascript"
    src="${resource(dir:'js/jquery.mobile-1.0b1',file:'jquery.mobile-1.0b1.min.js')}">
    </script>
 
<script type="text/javascript">
$(document).ready(function(){
    // le masquage pourrait être fait avec des CSS plus efficacement!
       var arr =new Array();
    var disp;
    $('.ui-slider').live('mouseup',function(index,el){
        
        arr = document.getElementsByClassName('listJD');
           for(var i =0; i < arr.length; i++){
               var obj = arr[i];
               if(parseInt(obj.children[0].children[0].children[0].children[5].textContent)==0){
                   obj.style.display ="none";
               }else{
                   if(parseInt(obj.children[0].children[0].children[0].children[5].textContent)<=parseInt($("#slider")[0].value)){
                       obj.style.display ="block";}
                   else{
                       obj.style.display ="none";}
                   
               }
           };
        });
var filterFeuillus =true;
    $("#feuillus").next('label').click(function(index,el){
        filterFeuillus = filterFeuillus==false?true:false;   
            if(filterFeuillus){disp ="block"}
                else
               {disp ="none"}
            arr = document.getElementsByClassName('listJD');
            for(var i =0; i < arr.length; i++){
                var obj = arr[i];
                if(obj.textContent
                        .indexOf("Feuillu")!=-1){
                    obj.style.display = disp;
                }else{    
                }
            }
    });
var filterConiferes =true;
    $("#coniferes").next('label').click(function(index,el){
        filterConiferes = filterConiferes==false?true:false;
            if(filterConiferes){disp ="block"}
                else
               {disp ="none"}
            arr = document.getElementsByClassName('listJD');
            for(var i =0; i < arr.length; i++){
                var obj = arr[i];
                if(obj.textContent
                        .indexOf("Conifère")!=-1){
                    obj.style.display = disp;
                }else{
                    
                }
            }
    });
});  
</script>
 
 
<style type="text/css">
.fr {
    font-weight:bold;
}
 
.lt {
    font-style:italic;
}
 
.fitToPort {
    width:100%;}
 
.Feuillu a.fr{
    background-image:url("../images/feuillu.jpg");
    background-repeat:no-repeat;
    background-position:010px;}
.Conifère a.fr{
    background-image:url("../images/conifere.jpg");
    background-repeat:no-repeat;
    background-position:010px;}
 
/* media queries from http://stuffandnonsense.co.uk/blog/about/hardboiled_css3_media_queries */
</style>
 
</head>
<body>
 
<!-- Start of FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF F F F F first page  1 1 1 1 1 1 -->
<divdata-role="page"id="home">
    <divdata-role="header"data-position="fixed">
        <h1>Arbr</h1>
    </div><!-- /header -->
 
    <divdata-role="content">
        <divdata-role="controlgroup"data-type="horizontal">
            <inputtype="checkbox"name="feuillus"id="feuillus"  checked/>
            <labelfor="feuillus">Feuillus</label>
            <inputtype="checkbox"name="coniferes"id="coniferes"  checked/>
            <labelfor="coniferes">Conifères</label>
        </div>
    
        <divdata-role="fieldcontain">
            <labelfor="slider">Distance  (m) :</label>
            <inputtype="range"name="slider"id="slider"value="14" min="0" max="15"/>
        </div>
    
        <uldata-role="listview"data-inset="true"data-filter="true">
            <g:each in="${arbreInstanceList}" status="i" var="arbreInstance">
                <liclass="listJD ${fieldValue(bean: arbreInstance, field: "type")}">
                    <ahref='#detail_${fieldValue(bean: arbreInstance, field: "id")}'class="ui-link-inherit fr">
                        ${fieldValue(bean: arbreInstance, field: "nomFrancais")}
                    </a>
                </li>
            </g:each>
        </ul>
    </div>
    <!-- /content -->
 
    <divdata-role="footer"data-position="fixed"data-fullscreen="true"style="text-align:center;">
        <divdata-role="navbar"data-type="horizontal">
            <ahref="#home"data-role="button"data-icon="grid"class="ui-btn-active">Liste</a>
            <ahref="#zone"data-role="button"data-icon="star">Zones</a>
            <ahref="#home"data-role="button"data-icon="gear">Préférences</a>
        </div>
    </div>
    <!-- /footer -->
</div>
<!-- /page -->
 
<!-- Start of second page  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  DETAILS  -->
<!-- DETAILS pages  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-->
<g:each in="${arbreInstanceList}" status="i" var="arbreInstance">
<divdata-role="page"data-url='detail_${fieldValue(bean: arbreInstance, field: "id")}'>
        <divdata-role="header"data-position="fixed">
            <h1>Arbr</h1>
        </div>
        <!-- /header -->
 
    <divdata-role="content"><ahref="#home"class="ui-link-inherit">
        <imgsrc='${fieldValue(bean: arbreInstance, field: "imageURL")}'alt=""/>
        <pclass="fr">${fieldValue(bean: arbreInstance, field: "nomFrancais")}</p>
        <pclass="lt">${fieldValue(bean: arbreInstance, field: "nomLatin")}</p>
        <pclass="en">${fieldValue(bean: arbreInstance, field: "nomAnglais")}</p>
        <pclass="zone">${fieldValue(bean: arbreInstance, field: "zone")}</p>
        <pclass="distance">${fieldValue(bean: arbreInstance, field: "distanceMinimale")}</p>
    </div>
    <!-- /content -->
 
    <divdata-role="footer"data-position="fixed"data-fullscreen="true"style="text-align:center;">
        <divdata-role="navbar"data-type="horizontal">
            <ahref="#home"data-role="button"data-icon="grid">Liste</a>
            <ahref="#zone"data-role="button"data-icon="star"class="ui-btn-active">Zones</a>
            <ahref="#home"data-role="button"data-icon="gear">Préférences</a>
        </div>
    </div>
    <!-- /footer -->
</div><!-- /page -->
</g:each>
 
<!-- Start of 3rd page  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-->
<!-- ZONE pages  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-->
<divdata-role="page"data-url="zone">
    <divdata-role="header"data-position="fixed">
    <h1>Arbr</h1>
    </div><!-- /header -->
 
    <divdata-role="content">
        <imgsrc="http://www.hydroquebec.com/arbres/images/img_zones_rusticite.gif"
            class="fitToPort"alt="Carte des zones de rusticité du Québec"/>
    </div><!-- /content -->
 
    <divdata-role="footer"data-position="fixed"data-fullscreen="true"
        style="text-align:center;">
        <divdata-role="navbar"data-type="horizontal">
            <ahref="#home"data-role="button"data-icon="grid">Liste</a>
            <ahref="#zone"data-role="button"data-icon="star">Zones</a>
            <ahref="#home"data-role="button"data-icon="gear"class="ui-btn-active">Préférences</a>
        </div>
    </div>
<!-- /footer -->
</div><!-- /page -->
 
</body>
</html>

Mobile avec iUI

Dans l'article précédent, je présente le cadre d'évaluation de plusieurs frameworks pour le développement d'application mobile. Cet article présente le détail pour iUI.

Le framework iUI est plus restreint que jQuery Mobile en terme de composant et il est particulièrement bien adapté aux interfaces utilisant les listes hiérarchiques (drilldown) typiques des applications pour téléphone intelligent (iPhone en particulier). La base, la liste, est bien faite et fonctionne rapidement. Cependant, on atteint rapidement les limites, comme l'utilisation d'un unique toolbar (en haut) et l'absence de tabBar (en bas).

Comme il s'agit d'un petit framework, les performances sont excellentes.

La documentation est faible. Néanmoins, j'ai aimé utiliser ce framework, mais j'ai dû adapter la conception de l'interface en fonction de ce qu'il supportait.

Pour qui cible de petits appareils, notamment les iPhones, il est possible d'obtenir une grande productivité rapidement.

 

arbrIuiHome.png

arbrIuiListe.png

 

 

arbrIuiDetail.png


<!DOCTYPE html>
<%-- indique au fureteur de traiter la page en HTML5--%>
<html>
<head>
<title>Arbres HQ</title>
<METAHTTP-EQUIV="EXPIRES"CONTENT="Mon, 22 Jul 200211:12:01 GMT">
<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8">
<metacharset="utf-8"/>
<linkrel="icon"type="image/ico"href="../images/logo.png">
<%-- les applications natives ne zoom pas, en general --%>
<%-- ces tags, propre a iOS permettent un look and feel plus natif--%>
<metaname="apple-touch-fullscreen"content="yes">
<metaname="apple-mobile-web-app-capable"content="yes">
<metaname="apple-mobile-web-app-status-bar-style"content="black">
<linkrel="apple-touch-startup-image"href="../images/splash.png"/>
<linkrel="apple-touch-icon"href="../images/logo.png">
<%-- iui specifics --%>
<metaname="viewport"
    content="width=device-width; initial-scale=1; maximum-scale=1; user-scalable=0;"/>
<linkrel="stylesheet"href="../iui.css"type="text/css"/>
<linkrel="stylesheet"href="../t/default/default-theme.css"
    type="text/css"/>
<script type="application/x-javascript" src="../iui.js"></script>
 
<script  type="text/javascript">
    function Filtrer(idOfSearchField){
        var arr =new Array();
        var countTreesShowingUp =0;
        arr = document.getElementsByClassName('list');
        for(var i =0; i < arr.length; i++){
            var obj = document.getElementsByClassName('list').item(i);
            if(obj.textContent
                    .indexOf(document.getElementById(idOfSearchField).value)==-1){
                obj.style.display ="none";
            }else{
                countTreesShowingUp++;
                obj.style.display ="block";
                document.getElementById('listCount').textContent = countTreesShowingUp +' arbre(s)';
            }
        }
    }// filtrer()
 
</script>
<style type="text/css">
body>.toolbar {
    top:0%;
    width:100%;
    position:fixed;
    z-index:99;
}
 
.inList {
    height:40px;
    margin-right:0.5em;
    float:left;
}
 
a.nomLatin {
    font-size:70%;
    /** mettre le code suivant si iPhone width = 360
    text-overflow: ellipsis ;
    overflow:hidden;
    white-space: nowrap;
    width:195px;
    margin-left:63px;
    **/
}
 
p.nomFr {
    font-size:60%;
    font-weight:normal;
    -webkit-margin-before:0em;
    color:#444;
    margin-left:72px;
}
 
.zone {
    position:relative;
    top:-10em;
    left:23px;
    width:2em;
    background:#42E01B;
    padding:0.2em;
    text-align:center;
    border:1pxsolid black;
    border-radius:5px;
}
 
.z5b {
    background:#cc7526;
}
 
.z5a {
    background:#fa9418;
}
 
.z4b {
    background:#adad2b;
}
 
.z4a {
    background:#e7d529;
}
 
.z3b {
    background:#9bc6cd;
}
 
.z3a {
    background:#84afb6;
}
 
.z2b {
    background:#b5dae2;
}
 
.z2a {
    background:#4ab4cc;
}
 
.z1b {
    background:#0095d7;
}
 
.z1a {
    background:#60c2ef;
}
 
.z0 {
    background:#c7e7f6;
}
 
.Coni,.Feui {
    position:relative;
    top:-10em;
    left:270px;
    width:2em;
    padding:0.2em;
    text-align:center;
    width:3em;
    height:3em;
}
 
.Coni span,.Feui span{
    display:none;
}
 
/** images de http://www.jenndickie.com/ctc/trees/index.html **/
.Coni {
    background-image:url(../images/conifere.png);
}
 
.Feui {
    background-image:url(../images/feuillu.png);
}
 
.fr,.lt,.en {
    margin-left:1em;
    margin-top:0px;
    margin-bottom:0.5em;
    position:relative;
    top:-55px;
}
 
.fr {
    font-weight:bold;
}
 
.lt {
    font-style:italic;
}
 
.en {
    font-size:80%;
    color:#444;
}
 
.distance {
position:relative;
top:-33px;
margin-left:1em;
font-weight:normal;
font-size:100;
-webkit-transform: scale(1) rotate(-24deg) translate(93px,4px) skew(47deg,0deg);
}
 
.distance:before{
    content:""
}
 
.carteLink {
    top:0.7em;
    position:relative;
    left:-1em;
}
 
.map {
    width:100%;
    }
</style>
 
</head>
 
<body>
<%-- T O O L B A R   there is only one toolbar in the app, this is a iUI limitation --%>
<divclass="toolbar">
    <h1id="pageTitle"></h1>
    <aid="backButton"class="button"href="#"></a>
    
</div>
 
<%-- M E N U   page d'accueil --%>
<ulid="home"title="Arbr"selected="true">
      <li><ahref="#list">Liste des arbres</a></li>
      <li><ahref="#favoris">Favoris</a></li>
      <li><ahref="#zone">Zone de rusticité</a></li>
      <li><ahref="#distance">Mesurer la distance minimale</a></li>
</ul>
 
<%-- L I S T E   de tous les arbres sur la page d'accueil --%>
<ulid="list"title="Liste">
    <li>
        
    <fieldset>
      <div>
         <labelfor="keyword">Filtrer :</label>
      <inputtype="text"name="q"id="keyword" placeholder="Nom"onkeyup="Filtrer(this.id);">
      </div>
   </fieldset></li>
    <liclass="group"id=listCount>Tous les arbres</li>
    <li><ahref="#search">Rechercher</a></li>
    <%-- on cré un li pour chacun des arbres --%>
    <g:each in="${arbreInstanceList}" status="i" var="arbreInstance">
        <liclass=list>
            <imgclass=inList src="${fieldValue(bean: arbreInstance, field: "vignetteURL")}"/>
            <aclass=nomLatin href="#${fieldValue(bean: arbreInstance, field: "id")}">${fieldValue(bean: arbreInstance, field: "nomLatin")}
                </a>
            <pclass=nomFr>${fieldValue(bean: arbreInstance, field: "nomFrancais")}</p>
        </li>
    </g:each>
</ul>
 
<%-- D E T A I L   de chacun des arbres, chacun sur sa page --%>
<g:each in="${arbreInstanceList}" status="i" var="arbreInstance">
<divid="${fieldValue(bean: arbreInstance, field: "id")}"
        title="${fieldValue(bean: arbreInstance, field: "nomLatin")}">
    <imgsrc="${fieldValue(bean: arbreInstance, field: "imageURL")}"/>
    <aclass="zone z${fieldValue(bean: arbreInstance, field: "zone")}"href="#zone">${fieldValue(bean: arbreInstance, field: "zone")}
        </a>
    <pclass="${fieldValue(bean: arbreInstance, field: "type")[0..3]}"><span>${fieldValue(bean: arbreInstance, field: "type")}
        </span></p>
    <pclass="fr">${fieldValue(bean: arbreInstance, field: "nomFrancais")}</p>
    <pclass="lt">${fieldValue(bean: arbreInstance, field: "nomLatin")}</p>
    <pclass="en">${fieldValue(bean: arbreInstance, field: "nomAnglais")}</p>
    <pclass="distance">${fieldValue(bean: arbreInstance, field: "distanceMinimale")}</p>
    <imgsrc="../images/distance.png"/>
    <aclass="favoris">Ajouter aux Favoris</a>
</div>
</g:each>
 
<%-- R E C H E R C H E R   écran de recherche --%>
<divid="search"title="Rechercher"class="panel">
   <fieldset>
      <divclass="row">
         <labelfor="select">Zone de rusticité</label>
         <ahref="#zone"class=carteLink>Carte</a>
         <selectid="select"name="selectname"size="1">
            <optionvalue="zone5b">5b</option>
            <optionvalue="zone5a">5a</option>
            <optionvalue="zone4b">4b</option>
            <optionvalue="zone4a">4a</option>
            <optionvalue="zone3b">3b</option>
            <optionvalue="zone3a">3a</option>
            <optionvalue="zone2b">2b</option>
            <optionvalue="zone2a">2a</option>
            <optionvalue="zone1b">1b</option>
            <optionvalue="zone1a">1a</option>
            <optionvalue="zone0">0</option>
         </select>
        
      </div>
      
      <divclass="row">
         <label>Conifères</label>
         <divclass="toggle"onclick="" toggled="true"><spanclass="thumb"></span><spanclass="toggleOn">1</span><spanclass="toggleOff">0</span></div>
      </div>
      <divclass="row">
         <label>Feuillus</label>
         <divclass="toggle"onclick="" toggled="true"><spanclass="thumb"></span><spanclass="toggleOn">1</span><spanclass="toggleOff">0</span></div>
      </div>
      <divclass="row">
         <labelfor="select">Distance minimale (mètres)</label>
         <selectid="select"name="selectname"size="1">
            <optionvalue="item15">15</option>
            <optionvalue="item14">14</option>
            <optionvalue="item13">13</option>
            <optionvalue="item12">12</option>
            <optionvalue="item11">11</option>
            <optionvalue="item10">10</option>
            <optionvalue="item9">9</option>
            <optionvalue="item8">8</option>
            <optionvalue="item7">7</option>
            <optionvalue="item6">6</option>
            <optionvalue="item5">5</option>
            <optionvalue="item4">4</option>
            <optionvalue="item3">3</option>
            <optionvalue="item3">2</option>
            <optionvalue="item0">0</option>
         </select>
      </div>
   </fieldset>
</div>
 
<divid="zone"title="Zone">
    <imgclass="map"src="http://www.hydroquebec.com/arbres/images/img_zones_rusticite.gif"/>
    <divclass="row">
        <p>Ma zone : 5b</p>
    </div>
</div>
 
<divid="distance"title="Mesurer distance minimale">
    <imgsrc="../images/distance.png"/>
</div>
 
</body>
</html>

Mobile avec Webapp-net

Dans l'article précédent, je présente le cadre d'évaluation de plusieurs frameworks pour le développement d'application mobile. Cet article présente le détail pour webapp-net.com.

Ce framework s'occupe principalement de l'interface et est axé sur les petits appareils, le look est celui de l’iPhone classique. Il supporte bien les listes simples et offre plusieurs composants d'interface intéressants, mais le manque de documentation et d'exemples le rende difficile à utiliser. La communauté autour de ce cadre logiciel semble inactive.

Au final, ce framework n'est pas très intéressant actuellement.

 

arbrWebappList.png

arbrWebappDetail.png

Voici le code avec webapp :

<!DOCTYPE html>
<!-- Webapp-net.com    est un framework sommaire peu documenté donc difficilement utilisable
    très simple a utilisée pour affichage de liste simple style iPhone -->
<html>
<head>
    <title>Demo Webapp.net</title>
    <metahttp-equiv="Content-Type"content="text/html; charset=UTF-8">
    <metacharset="utf-8"/>
    <metaname="viewport"
        content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
    <metaname="apple-touch-fullscreen"content="yes">
    <metaname="apple-mobile-web-app-capable"content="yes">
    <metaname="apple-mobile-web-app-status-bar-style"content="black">
    <linkrel="apple-touch-startup-image"href="../images/splash.png"/>
    <linkrel="apple-touch-icon"href="../images/logo.png">
    <linkrel="icon"type="image/ico"href="../images/logo.png">
    <linkrel="stylesheet"href="${resource(dir:'Design',file:'Render.css')}"/>
    <script type="text/javascript"    src="${resource(dir:'Action',file:'Logic.js')}">
    </script>
<style>
.XXXinList {
    width:8em;}
.nomLatin {font-style:italic;}
</style>
</head>
 
<body>
<divid="WebApp">
    <divid="iHeader"><ahref="#"id="waBackButton">Retour</a><span
        id="waHeadTitle">Arbres HQ</span><ahref="#"id="waHomeButton">Home</a>
    <ahref="#"onclick="return WA.HideBar()"></a>
    <!-- Add an hidden form in the header. Will be shown pressing search button -->
    
    <formclass="iForm"id="headForm"action=""onsubmit="searchLoader()">
        <ahref="#"rel="action"id="gogo"class="iButton iBAction">Rechercher</a>
        <ahref="#"rel="back"class="iButton iBClassic">Annuler</a>
        <fieldset>
            <legend>Recherche</legend>
            <inputtype="search"name="search" placeholder="Chercher..."/>
        </fieldset>
    </form>
</div>
    
<divid="iGroup">
    <divclass="iLayer"id="waHome"title="Liste des arbres">
    <ahref="#"rel="action"onclick="return WA.Form('headForm')"id="oki"
        class="iButton iBClassic"><span>Rechercher</span></a>
    <divclass="iMenu">
        <ulclass="iArrow">
         <g:each in="${arbreInstanceList}" status="i" var="arbreInstance">
            <li><ahref="#_Arbre${fieldValue(bean: arbreInstance, field: "id")}"><imgclass=inList src="${fieldValue(bean: arbreInstance, field: "vignetteURL")}"/>
            <emclass="nomLatin">${fieldValue(bean: arbreInstance, field: "nomLatin")}</em>
            <small>${fieldValue(bean: arbreInstance, field: "nomFrancais")}</small></a></li>
        </g:each>    
        </ul>
    </div>
</div>
        
<g:each in="${arbreInstanceList}" status="i" var="arbreInstance">    
    <divclass="iLayer"id="waArbre${fieldValue(bean: arbreInstance, field: "id")}"title="Détail">
        <divclass="iBlock">
        <h1class="nomLatin">${fieldValue(bean: arbreInstance, field: "nomLatin")}</h1>
        <imgclass=inList src="${fieldValue(bean: arbreInstance, field: "imageURL")}"/>
        <p>${fieldValue(bean: arbreInstance, field: "type")}</p>
        <p>${fieldValue(bean: arbreInstance, field: "nomFrancais")}</p>
        <p>${fieldValue(bean: arbreInstance, field: "nomAnglais")}</p>
        <p>Zone : ${fieldValue(bean: arbreInstance, field: "zone")}</p>
        <p>Distance minimale : ${fieldValue(bean: arbreInstance, field: "distanceMinimale")}</p>
        </div>
    </div>
</g:each>
    
</div>
</div>
</body>
</html>