jeudi 26 novembre 2015

JSTL c:if doesn't work inside a JSF h:dataTable

Probléme:

 
<h:dataTable value="#{bean.items}" var="item" width="80%">
    <h:column>
        <f:facet name="header">
            <h:outputText value="State" />
        </f:facet>

        <c:if test="#{item.state != 'Finish'}">
            <h:outputText value="Missing value" />
        </c:if>
        <c:if test="#{item.state == 'Finish'}">
            <h:outputLink value="myLink">
                <h:outputText value="Value = #{item.state}" />
            </h:outputLink>
        </c:if>
    </h:column>
</h:dataTable>
 
 

Solution :

JSTL tags are evaluated during building of the view, not during rendering of the view. You can visualize it as follows: Whenever a view tree get created for the first time, all JSTL tags are executed and the result is a view with only JSF components. Whenever a view tree get rendered, all JSF components get executed and the result is HTML. So: JSF+JSTL doesn't run in sync as you'd expect from the coding. JSTL runs from top to bottom first, hands the result to JSF and then it's JSF's turn to run from top to bottom again. This may lead to unexpected results in JSF iterating components like UIData because the row data (in your particular case the #{item} object) is not available while JSTL runs.
In a nutshell: Use JSTL to control flow of JSF component tree building. Use JSF to control flow of HTML output generation.
You want to use the rendered attribute here.

<h:outputText value="Missing value" rendered="#{item.state ne 'Finish'}" /> <h:outputLink value="myLink" rendered="#{item.state eq 'Finish'}"> <h:outputText value="Value = #{item.state}" /> </h:outputLink>
 

jeudi 23 octobre 2014

Openerp Module Development .

Un module openerp est généralement composé de fichiers et repertoires suivants.
 module_name/
    ├── module_name.py
    ├── module_name_view.xml
    ├── __init__.py
    ├── __openerp__.py
    ├── reports
    ├── wizards
    └── workflows
init : Indique que le dossier est un package
module_name.py : Il contient les objects de votre module
module_name_view.xml : Gere les donnees via une definition d'interface (view, les menu, les interactions ...)
openerp : Decrit le module (nom , version, auteur, dependances,...)
reports : Dossier contenant les objest et fichiers lies au reporting
wizards : Dossier contenant les objets definissant les interactions client-server (voir)
workflow : Dossier contenant la definition d'un workflow (voir)

A- Un hello world

Pour notre hello world j'ai pensé à un module nous permettant d'enregistrer une famille et ses membres .
A vos terminaux !!!
Creer un dossier family dans votre espace de travail avec les fichier suivants :
1- openerp.py dont le contenu est le suivant :
{
    "name":"family", # le nom du module, il apparait dans la liste des modules
    "version":"1.0", # la version
    "author":"Josue", # l'auteur
    "website":"none", # site web
    "category":"Generic Modules/Others", # category du module
    "depends":["base"], # liste des modules de dependances
    "description":"Simple module permettant d'enregistrer une famille est ses memebres",
    "update_xml":["family_view.xml"], #liste des vue, dans notre cas family_view.xml
    "demo_xml":[],
    "init_xml":[],
    "active":False,
    "installable":True,
}
NB : Tous les modules dependent de base .
2- init.py contenant ceci :
import family
Simple importation d'un module python
3- family.py
from osv import osv, fields

class family_family(osv.osv):
    _name = 'family.family'
    _columns = {
        'name': fields.char('Family Name',size=128),
        'member_ids': fields.one2many('family.member','family_id','Members'),
    }


family_family()

class family_member(osv.osv):
    _name = 'family.member'
    _columns = { # dictionnaire des champs de l'object
        'firstname': fields.char('FirstName',size=128),
        'lastname': fields.char('LastName',size=128),
        'family_id': fields.many2one('family.family','Family',ondelete='cascade'),
        'gender': fields.selection(  # definition d'un champ de selection
            [('male','Male'),
            ('female','Female'),
            ],'Gender',readonly=False,select=True),
    }
    # default : dictionnaire des valeurs par defaut
    _defaults = {
        'lastname' : lambda self, cr, uid, context : self.pool.get('family.family').browse(cr, uid, context['family_id']).name if context and 'family_id' in context else None,
        'gender' : lambda *a: 'male', # "male" sera la valeur par defaut du champ "gender"
    }

family_member() #instanciation de la classe , __N'oubliez jamais de le faire__
Ce fichier decrit les objects du modules et leur caracteristiques
Documentation sur l'objet osv
Documentation sur l'objet fields

explications :

_defaults['lastname'], j'ai utilisé une fonction lambda afin de permettre que le champ lastname soit automatiquement rempli en se servant du context family_id definit dans le fichier family_view.xml au sein de l'objet family. Mais cela ne se fera que dans la condition ou un membre sera crée à partir du menu(vue,interface) d'une famille.
4- family_view.xml
<?xml version="1.0" ?>

<openerp>
        <data>

    <menuitem id="family" name="Family" />

    <!--Family-->
    <record model="ir.ui.view" id="family_form"> <!-- definition d'une vuew dont l'id est "family_form"--> 
        <field name="name">family.form</field> <!-- nom de la vue -->
        <field name="model">family.family</field> <!-- classe sur laquelle on se base pour creer la vue (une des classes de "family.py", dans notre cas "family.family")-->
        <field name="type">form</field> <!--type de vue : form/tree/graph -->
        <field name="arch" type="xml">
            <form string="Family" > <!-- declaration d'un formulaire -->
                <field name="name"/>  <!-- declaration d'un champ -->
                <notebook colspan="4"> 
                    <page string="Members">
                        <field name="member_ids" domain="[('family_id','=',active_id)]" context="{'family_id':active_id}"/> <!-- definition d'un context -->
                    </page>
                </notebook>
            </form>
        </field>
    </record>

    <record model="ir.ui.view" id="family_tree">
        <field name="name">family.tree</field>
        <field name="model">family.family</field>
        <field name="type">tree</field> <!--type de vue : tree, correspond a l'affichage en liste d'elements --> 
        <field name="arch" type="xml">
            <tree string="Family" > <!-- on declare "tree" cette fois ci et non un "form"-->
                <field name="name"/>
                <field name="member_ids"/> <!-- declaration des champs -->
            </tree>
        </field>
    </record>

    <!-- Notez que tous les champs declarés ici sont dans family.py, sinon une erreur sera levée -->

    <!--Definition d'une action -->

    <record model="ir.actions.act_window" id="action_family"> <!-- le model est bien différent : "ir.actions" et non "ir.ui.view" -->
        <field name="name">Family</field> <!-- nom de l'action -->
        <field name="res_model">family.family</field> <!-- model sur lequel se base l'action -->
        <field name="view_type">form</field> 
        <field name="view_mode">tree,form</field>
    </record>

    <!-- Declaration d'un menu -->
    <menuitem id="family_menu" name="Family" parent="family" action="action_family"/>
    <!--
        id : identifiant du menu
        name : nom du menu
        parent : le menu dont depend celui-ci, il sera donc un sous-menu 
        action : action relier au menu
    -->
    <!-- "action_family" correspond à l'id de l'acton déclarée pécedement -->


    <!--Members-->

    <record model="ir.ui.view" id="member_form">
        <field name="name">member.form</field>
        <field name="model">family.member</field>
        <field name="type">form</field>
        <field name="arch" type="xml">
            <form string="Member" >
                <field name="firstname"/>
                <field name="lastname"/>
                <field name="gender" />
                <field name="family_id"/>
            </form>
        </field>
    </record>

    <record model="ir.ui.view" id="member_tree">
        <field name="name">member.tree</field>
        <field name="model">family.member</field>
        <field name="type">tree</field>
        <field name="arch" type="xml">
            <tree string="Member" >
                <field name="firstname"/>
                <field name="lastname"/>
                <field name="gender" />
                <field name="family_id"/>
            </tree>
        </field>
    </record>

    <record model="ir.actions.act_window" id="action_member">
        <field name="name">Member</field>
        <field name="res_model">family.member</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form</field>
    </record>

    <menuitem id="member_menu" name="Members" parent="family" action="action_member"/>

    </data>
</openerp>