Question pointue sur les bindings?

cestlogiquecestlogique Membre
23:43 modifié dans API AppKit #1
Bonjour à  tous!

Voici une question un peu pointue sur les bindings:

J?ai un arrayController d?objets A et un d?objets B.
Les objets A ont une relation to-many vers des objets B.
Les objets B ont un attribut "tag".
Mes objets A apparaissent dans une tableView (dont les colonnes sont bindées à  l?arrayController A) avec leurs attributs (nom, date?).
La dernière colonne de la tableView est bindée à  l?autre arrayController pour afficher l?attribut "valeur" des objets B.
Sous la tableView, j?ai un popubButton bindé au 2e arrayController qui permet de choisir quel objet B afficher, en le sélectionnant par son "tag".

Mon but est d?afficher dans la dernière colonne la valeur de l?objet B dont le tag est x appartenant à  chaque objet A se trouvant dans la table.

1) Suis-je fou?
2) L?idée de binder des colonnes à  plusieurs arrayControllers différents est-elle absurde?
3) Sinon, comment faire pour que ça marche (j?ai essayé plein de combinaisons de bindings? sans succès :'( ).

Dois-je m?orienter vers un fetch ou un prédicate? Je n?ai toujours pas compris comment les utiliser, c?est pas faute d?avoir lu la doc?

Merci pour toute info susceptible de m?aider!  ::)

Réponses

  • laurrislaurris Membre
    septembre 2006 modifié #2
    Réponses:
    1/ Les fous ne doutent jamais de leur normalité. Si tu t'interroges c'est que tu n'es pas fou.
    2/ Si tes objets B sont indépendants des objets A, alors je ne vois pas trop où est le problème. Ta dernière colonne est bindée à  la selection du 2eme AC et le selectionIndex du popup du bas est bindé au selectionIndex. Non ?

    Tout se complique si tes objets A ont une relation 1 to many vers les objets B. C'est ce que tu écris plus haut mais est-ce vraiment le cas ? par exemple, est-ce que dans ta dernière colonne tu pourrais avoir un objet B différent selon la ligne ou le tag selectionné est global pour toute la colonne ?

    Dans ce cas il te faut une structure en arbre pour avoir une copie des objets B différente pour chaque selection des objets A.

  • cestlogiquecestlogique Membre
    septembre 2006 modifié #3
    Hello! Merci pour ta réponse 
    dans 1157964385:

    2/ Si tes objets B sont indépendants des objets A, alors je ne vois pas trop où est le problème. Ta dernière colonne est bindée à  la selection du 2eme AC et le selectionIndex du popup du bas est bindé au selectionIndex. Non ?

    Oui mais dans ce cas la dernière colonne affiche la valeur de l'objet sélectionné à  toute les lignes du tableau, si on clique sur une autre ligne, sa valeur s'inscrit dans toutes les autres.
    dans 1157964385:

    Tout se complique si tes objets A ont une relation 1 to many vers les objets B. C'est ce que tu écris plus haut mais est-ce vraiment le cas ? par exemple, est-ce que dans ta dernière colonne tu pourrais avoir un objet B différent selon la ligne ou le tag selectionné est global pour toute la colonne ?

    Oui, chaque objet A peut avoir 1 ou plusieurs objets B. Dans la dernière colonne on doit avoir un objet B différent selon la ligne (chaque objet A a ses propres objets B) mais je souhaite que seuls les objets B partageant un tag donné apparaissent, en face de leur objet A parent.

    J'ai finalement retrouvé un exemple de filterPredicate que je vais essayer d'adapter ce soir, je pense que ça fonctionnera mais c'est un peu lourd: ça comprend la création d'un value transformer (pour renvoyer la string de l?article de menu sélectionné sous forme de predicate) et tout!  :)
  • cestlogiquecestlogique Membre
    23:43 modifié #4
    Rebonjour.

    Le problème est insoluble: si ma denière colonne est bindée en selection, un seul élément de mon 2e AC pouvant être sélectionné, chaque ligne a la même valeur. Si je binde en arrangedObjects, ça me dit que ça ne peut pas exprimer un array sous forme de nombre! Logique!!

    Voilà  pourquoi je voulais créer un attribut "monObjetBDontLeTagEstSelectionné" qui serait to-one et que je pourrais atteindre directement via le 1er AC.

    Le problème c'est qu'il faut que ma classe perso pour ces objets B sache quel est le tag sélectionné dans l'interface, mais les outlets ne fonctionnent pas: impossible de faire glisser le fichier .h vers IB, ça me dit que cette classe (NSManagedObject) n'est pas reconnue.

    Comment récupérer une info d'interface dans une entité coreData???
  • 23:43 modifié #5
    dans 1158000629:

    Comment récupérer une info d'interface dans une entité coreData???


    ça c'est mal, entité CoreData = Model, info d'interface = Controller. ça ne peut pas se mélanger selon la sacro-sainte doctrine du MVC.

    Perso, c'est typiquement je considère que c'est le genre de cas où il ne faut pas trop compter sur les bindings. Le plus simple est encore de ne pas faire de bindings sur ta dernière colonne et utiliser un bon vieux dataSource (tu peux le faire pour une seule colonne). N'oublie juste pas de faire un outlet vers l'array controller qui alimente ta table de manière à  pouvoir utiliser les arrangedObjects de ce dernier pour "alimenter" ton dataSource.
  • laurrislaurris Membre
    septembre 2006 modifié #6
    dans 1158000629:

    Rebonjour.

    Le problème est insoluble: si ma denière colonne est bindée en selection, un seul élément de mon 2e AC pouvant être sélectionné, chaque ligne a la même valeur. Si je binde en arrangedObjects, ça me dit que ça ne peut pas exprimer un array sous forme de nombre! Logique!!

    Voilà  pourquoi je voulais créer un attribut "monObjetBDontLeTagEstSelectionné" qui serait to-one et que je pourrais atteindre directement via le 1er AC.

    Le problème c'est qu'il faut que ma classe perso pour ces objets B sache quel est le tag sélectionné dans l'interface, mais les outlets ne fonctionnent pas: impossible de faire glisser le fichier .h vers IB, ça me dit que cette classe (NSManagedObject) n'est pas reconnue.

    Comment récupérer une info d'interface dans une entité coreData???


    Oula c'est du multicouche ton truc. Je ne peux pas répondre à  tout mais juste à  cette phrase : 
    "Si je binde en arrangedObjects, ça me dit que ça ne peut pas exprimer un array sous forme de nombre! "
    c'est un problème que j'ai déjà  rencontré: le binding te demande un objet alors que tu as une collection. Si tu pratiques un filtrage avec un predicate, obligatoirement tu récupères une collection de 0 ou plus objets. La seule solution que je vois, c'est un NSValueTransformer pour extraire le premier objet de la collection.
    Tant qu'à  faire, tu peux aussi réaliser le filtrage + l' extraction d'objet unique dans le NSvalueTransformer.

    C'est ce que je ferais mais je n'ai peut-être pas intégré tous les problèmes.

  • cestlogiquecestlogique Membre
    23:43 modifié #7
    Merci pour vos réponses!

    dans 1158050332:

    ça c'est mal, entité CoreData = Model, info d'interface = Controller. ça ne peut pas se mélanger selon la sacro-sainte doctrine du MVC.
    Le plus simple est encore de ne pas faire de bindings sur ta dernière colonne et utiliser un bon vieux dataSource (tu peux le faire pour une seule colonne).


    Eh oui, je sais, mais comme je ne trouvais pas de solution! Enfin, le dataSource j'en ai jamais fait et ça m'embête un peu de devoir en assurer la maintenance: il faudrait que j'ordonne les données en fonction de l'autre AC... Et surtout comment être sûr que tout est bien synchronisé avec coreData?

    Bref, j'ai tenté l'inverse: binder toutes les colonnes à  l'AC des objets B qui ont une relation to-one vers leur A parent dont je peux donc obtenir les attributs. Normalement ça devrait fonctionner: les éléments B sont triés selon leur tag via un value transformer qui convertit la valeur du popup en predicate: je n'affiche que ceux dont le tag est 1 par exemple; chacun affiche son élément A puisqu'il n'en a qu'un seul; ensuite je mets un sortDescriptor pour ordonner le tout selon le nom de chaque entité A...

    Bon bien sûr ça marche pas alors ce soir je vais tenter de commenter le tout et d'insérer le code pas à  pas.  :o


  • cestlogiquecestlogique Membre
    23:43 modifié #8
    Bon merci à  vous, j?ai réussi à  contourner le problème en modifiant légèrement mon MOM, en insérant une entité intermédiaire.

    Pour les personnes intéressées, voici le code de mon valueTransformer: on binde le selectedTag d?une matrice de radioButtons aux défaults, on binde cette valeur à  l?AC avec le value transformer suivant:

    + (Class)transformedValueClass;<br />{<br />&nbsp; &nbsp; return [NSPredicate class];<br />}<br /><br />+ (BOOL)allowsReverseTransformation;<br />{<br />&nbsp; &nbsp; return NO;<br />}<br /><br />- (id)transformedValue:(id)value;<br />{<br />&nbsp; &nbsp; int selectedTag;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; if (value == nil) return nil;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; if ([value respondsToSelector: @selector(intValue)]) {<br />&nbsp; &nbsp; &nbsp; &nbsp; selectedTag = [value intValue];<br />&nbsp; &nbsp; } else {<br />&nbsp; &nbsp; &nbsp; &nbsp; [NSException raise: NSInternalInconsistencyException<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; format: @&quot;Value (%@) does not respond to -intValue.&quot;, [value class]];<br />&nbsp; &nbsp; }<br /><br />&nbsp; &nbsp; return [NSPredicate predicateWithFormat:[NSString stringWithFormat:@&quot;tag == %i&quot;, selectedTag]];<br />&nbsp; &nbsp; <br />}<br />
    
  • aranaudaranaud Membre
    23:43 modifié #9
    Merci pour ce bout de code.

    Juste une question,
    <br />+ (Class)transformedValueClass;<br />{<br />&nbsp; &nbsp; return [NSPredicate class];<br />}<br />
    

    A quoi ca sert ? Et comment peut-on savoir la classe qui faut mettre ?
  • AliGatorAliGator Membre, Modérateur
    23:43 modifié #10
    Ben avec ce bout de code tu crées un NSValueTrasnformer. Par définition, ça transforme une valeur d'une certaine classe (non précisée, du moment qu'elle répond aux messages que tu as besoin de lui envoyer) en une autre valeur d'une autre classe, qu'il faut, elle, préciser.

    Par exemple tu peux faire un NSValueTransformer qui transforme tout objet répondant au message/selecteur "intValue" en une couleur, disons rouge si la intValue est négative, bleu si positif, noir si nul. (Ce n'est qu'un exemple).

    Pour cela il faut 2 choses : indiquer à  ta classe NSValueTransformer que tu es en train de créer que ce que tu vas retourner c'est une NSColor (puisque tu veux que ton transformer te retourne une NSColor à  partir d'un objet quelconque), et ça c'est le but de la méthode transformedValueClass qui indique la classe retournée, et il faut aussi au moment de la trasnformation vérifier que l'objet qu'on essaye de transformer répond au sélecteur que l'on a besoin d'appeler (intValue dans mon exemple), et si ce n'est pas le cas, générer une exception.


    Là  il se trouve que pour le code donné par cestlogique, le but est de transformer un objet sachant te retourner une intValue, pour le transformer en NSPredicate. Donc c'est bien NSPredicate qu'il faut indiquer comme classe, puisque c'est un NSPredicate que tu veux en retour ;)

    Plus d'infos sur les ValueTransformers ici
  • aranaudaranaud Membre
    23:43 modifié #11
    Merci de cette éclaircissement.
Connectez-vous ou Inscrivez-vous pour répondre.