IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

AngularJS Discussion :

Partage de valeurs entre la vue principale et les vues partielles


Sujet :

AngularJS

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut Partage de valeurs entre la vue principale et les vues partielles
    Bonjour,
    L'objectif final est qu'une partie du menu ne s'affiche que si l'utilisateur est connecté, voici comment je m'y suis pris.
    J'ai mis un ng-show="isConnected" sur l'élément <ul>.
    Le menu dépend du contrôleur mainCtrl placé sur <body> dans lequel j'initialise isConnectd à false.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    app.controller('mainCtrl', ['$scope', function ($scope) {
            $scope.isConnected = false;
        }]);
    Lorsqu'on clique sur le bouton Connexion j'appelle une vue partielle connexion.html qui a son contrôleur connexionCtrl. Si le login et pwd sont OK je passe isConnected à true :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            $scope.seConnecter = function () {
                var login = $scope.login;
                var pwd = $scope.pwd;
                $scope.isConnected = Connexion.seConnecter(login, pwd);
                if ($scope.isConnected)
                    $location.path('/home');
            };
    J'ai vérifié isConnected passe bien à true dans la fonction et j'affiche bien la vue partielle, mais le menu ne s'affiche pas et dans la vue partielle home.html isConnected est à false.
    Je tourne en rond et une petite aide serait la bienvenue.
    Merci d'avance

  2. #2
    Expert confirmé
    Avatar de Marco46
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2005
    Messages
    4 419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2005
    Messages : 4 419
    Par défaut
    Il manque le code de la vue pour bien comprendre ton problème (ça peut venir de comment est conçue ta vue, où est déclaré le contrôleur, etc ...).

    Déjà je vois que la méthode seConnecter n'est pas implémentée dans mainCtrl ce qui est vraisemblablement la source de ton problème.

    D'une manière générale, si tu utilises le service $scope au lieu de la syntaxe Controller As, il ne faut pas utiliser de variables primitives mais toujours des objets complexes (du fait de l'héritage des contrôleurs entre eux combiné à l'héritage par prototype de js).
    Donc $scope.monObjet.isConnected au lieu de $scope.isConnected.

    Donc ici ton problème (mais je demande confirmation, il faudrait que tu publies le contexte de ta méthode seConnecter (le contrôleur où elle est implémenter) et ta vue) c'est vraisemblablement que le scope de isConnected n'est pas le même que celui de seConnecter.

    La meilleure solution pour contrer ça (outre le fait d'utiliser Controller As plutôt que le service $scope) c'est de toujours passer par des services pour implémenter de la logique.

    Ca donnerait quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    (function (angular) {
        'use strict';
     
        angular
            .module('myModule')
            .factory('MyLoginService', myLoginService);
     
        myLoginService.$inject = ['Connexion', '$location'];
     
        function myLoginService(Connexion, $location) {
     
    		var instance = {
    			isConnected : false,
    			login: login
    		};
     
    		return instance;
     
            function login(_login, _pwd) {
                instance.isConnected = Connexion.seConnecter(login, pwd);
                if (instance.isConnected)
                    $location.path('/home');
            }
        }
     
    })(angular);
    Ensuite dans tes contrôleurs tu injectes ton service et tu le bind sur ton scope :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $scope.loginServiceInstance = MyLoginService;
    Et dans tes templates tu n'as plus qu'à appeler le service :

    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <div ng-if="loginServiceInstance.isConnected"></div>
     
    <button ng-click="loginServiceInstance.login(login, pwd)"></button>

    Je ne sais pas si je suis clair.


    Une dernière chose, ng-show ne fait qu'appliquer un display-none à ton élément. Par conséquent le menu est toujours présent dans le DOM. Dans ton cas un ng-if serait plus approprié.

    EDIT : A la relecture du précise bien que la méthode de login est bien dans un contrôleur à part. La solution est donc bien de faire un service contenant la logique pour partager le contexte entre les contrôleurs.
    D'une manière générale il ne faut jamais écrire de logique dans les contrôleurs. Si tu as un seul if alors il te manque un service.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut
    Bonjour,
    Tout d'abord merci pour cette réponse à la fois complète et bien structurée.
    Je n'utilise pas Controller As, mais je vais y passer.
    Je pense avoir résolu mon problème, mais je suis pas sûr que ma solution respecte les bonnes pratiques :
    Voici le code de mon service :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    connexionService.factory('Connexion', [function () {
            var seConnecter = function (login, pwd) {
                var connected = false;
                . . .
                return connected;
            };
            return {
                seConnecter: seConnecter
            }
        }]);
    Remarque : là aussi je vais adopter la bonne pratique qui consiste à déclarer un objet qui expose des services comme tu le montre dans ton exemple.
    Le contrôleur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    app.controller('connexionCtrl', ['$rootScope', '$scope', 'Connexion', '$location', function ($rootScope, $scope, Connexion, $location) {
            $scope.seConnecter = function (login, pwd) {
                $scope.erreurLogin = "";
                $rootScope.isConnected = Connexion.seConnecter(login, pwd);
                if ($rootScope.isConnected)
                    $location.path('/home');
                else
                    $scope.erreurLogin = "Login ou mot de passe erroné";
            };
        }]);
    J'utilise rootScope pour stocker isConnected.
    Ma vue principale :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <html ng-app="app">
        <head>
            . . .         
        </head>
        <body ng-controller="mainCtrl" class="body">
    <div class="container">
                <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
                    <div class="container-fluid">
                        <div class="navbar-header">
                            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-target">
                                . . .
                        </div>
                        <div class="collapse navbar-collapse" id="navbar-collapse-target">
                            <ul class="nav navbar-nav" ng-show="isConnected">
    Remarque : là aussi je vais changer et utiliser user.isConnected.
    Au final est-ce que l'utilisation de rootScope est une bonne pratique ou pas ?
    Merci.

  4. #4
    Expert confirmé
    Avatar de Marco46
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2005
    Messages
    4 419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2005
    Messages : 4 419
    Par défaut
    L'utilisation du rootScope permet de résoudre ton problème puisque ton problème au final c'est que tu as les données éclatées sur 2 scopes différents.

    Je n'en ai pas parlé parce que justement ce n'est pas une bonne pratique.

    La bonne pratique c'est de n'avoir aucune autre variable que celles des instances des services dans les contrôleurs. Et surtout aucune implémentation (donc pas de $scope.seConnecter).

    Tout simplement parce que :
    1/ ce que tu implémentes dans un scope n'est pas réutilisable ailleurs.
    2/ toute implémentation dans un scope est impossible à tester.

    Au final, un contrôleur ne doit contenir que du binding de services et une fonction d'initialisation (puisqu'un contrôleur est en définitive la fonction d'initialisation d'un scope donné, c'est le constructeur du scope si tu préfères).

    Un exemple de contrôleur écrit avec les bonnes pratiques :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    (function (angular) {
        "use strict";
     
        angular
            .module('myModule')
            .controller('ExampleController', exampleController);
     
        exampleController.$inject = ['someDataService'];
     
        function exampleController(someDataService) {
     
            var vm = this;
     
            init();
     
            function init() {
                someDataService.load().then(function (_data) {
                    vm.data = _data;
                });
            }
        }
     
    }(angular));

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 179
    Par défaut
    Bonjour,
    Donc si j'ai bien compris, l'utilisation de rootScope n'est pas une mauvaise chose en soi, mais il ne faut le faire qu'au sein d'un service et non pas dans un contrôleur.
    Concernant ce problème de partage de valeurs entre différents contextes (scopes) j'ai vu pas mal de solutions prônant l'abonnement d'une variable à un gestionnaire d'évènements via le module $on.
    Même si ce genre de solutions paraissent plus élégantes, elles me semble plus complexes à mettre en oeuvre et sont basées (sauf si j'ai mal compris) sur le fait que chaque scope ait déclaré la même variable (même nom), ce qui implique que si on change de nom dans un scope il faut le faire dans tous les autres ?

  6. #6
    Expert confirmé
    Avatar de Marco46
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2005
    Messages
    4 419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2005
    Messages : 4 419
    Par défaut
    Citation Envoyé par clem_alain Voir le message
    Bonjour,
    Donc si j'ai bien compris, l'utilisation de rootScope n'est pas une mauvaise chose en soi, mais il ne faut le faire qu'au sein d'un service et non pas dans un contrôleur.
    Disons que comme il ne faut pas mettre de logique dans un contrôleur il n'y a pas de raison d'utiliser le rootScope.

    Un usage fréquent du rootScope c'est, lors de l'initialisation de la webapp, de stocker des informations et des instances de services qui sont vraiment globaux à l'application. Ca évite d'injecter de nombreuses fois les mêmes infos.

    Mais même là il n'y a rien qui ne soit pas faisable avec des services. Je te recommande vraiment de tout mettre dans les services, 99% de ton code doit être dans les services car ça se mock très facilement dans les TU et c'est full réutilisable.

    Citation Envoyé par clem_alain Voir le message
    Concernant ce problème de partage de valeurs entre différents contextes (scopes) j'ai vu pas mal de solutions prônant l'abonnement d'une variable à un gestionnaire d'évènements via le module $on.
    Oui ya des devs qui aiment utiliser le système d'évènements ... jusqu'à ce qu'ils essayent d'écrire des TU sur ce genre de code
    Parfois ya pas le choix (routage), mais dans le mesure du possible il faut éviter d'utiliser les events. Là aussi les services rempliront 99% des usages.

    Citation Envoyé par clem_alain Voir le message
    Même si ce genre de solutions paraissent plus élégantes, elles me semble plus complexes à mettre en oeuvre et sont basées (sauf si j'ai mal compris) sur le fait que chaque scope ait déclaré la même variable (même nom), ce qui implique que si on change de nom dans un scope il faut le faire dans tous les autres ?
    C'est pas plus élégant d'utiliser des events, ça parait plus simple quand il n'y a que un ou deux events à gérer. Dès lors que tu vas commencer à avoir des events qui trigger d'autres events qui trigger d'autres events, etc ..., là ça va devenir l'enfer (effet spaghetti).

    De plus, cela crée une adhérence très forte sur le scope ce qui a plusieurs conséquences :
    - adhérence sur la hiérarchie des scopes pour la propagation
    - difficile à utiliser dans des services (injection de scope ou rootScope dans un service pas super tip top)
    - la notion de scope disparait dans Angular 2, il est recommandé d'utiliser la syntaxe Controller As.

    Un bon truc à retenir, quand tu ne sais pas où écrire du code, écrit le dans un service. 99% du temps tu seras dans le vrai. Teste le unitairement et tu seras blindé.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [CR XI] Partage d'une liste de valeurs entre état et sous-état
    Par EXPE43 dans le forum SAP Crystal Reports
    Réponses: 1
    Dernier message: 30/12/2014, 13h01
  2. Architecture entre vue principale et vues secondaire
    Par stephane.06 dans le forum Agents de placement/Fenêtres
    Réponses: 0
    Dernier message: 12/12/2011, 11h05
  3. Voir les vues mais pas les tables
    Par Yusuke26 dans le forum SQL
    Réponses: 24
    Dernier message: 21/01/2011, 16h56
  4. Réponses: 4
    Dernier message: 24/02/2009, 13h56
  5. Réponses: 3
    Dernier message: 30/03/2004, 09h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo