81 votes

Développer une application AngularJS avec un ensemble dynamique de modules

J'ai une application avec une mise en page complexe où l'utilisateur peut placer (glisser/déposer) des widgets (en choisissant parmi un ensemble prédéfini de plus de 100 widgets) où chaque widget est une implémentation personnalisée qui affiche un ensemble de données (récupérées par appel REST) d'une manière spécifique. J'ai lu des tonnes d'articles de blog, de questions sur stackoverflow et la documentation officielle d'AngularJS, mais je n'arrive pas à comprendre comment je dois concevoir mon application pour répondre à ces exigences. En regardant les applications de démonstration, il y a un seul module (ng-app) et lors de sa construction dans le fichier .js les modules dépendants sont déclarés comme ses dépendances, cependant j'ai un grand ensemble de widgets et d'une certaine manière il n'est pas conseillé de les décrire tous ici. J'ai besoin de suggestions pour les questions suivantes :

  • Comment dois-je concevoir mon application et mes widgets ? Dois-je avoir un module AngularJS distinct ou chaque widget doit-il être une directive du module principal ?
  • Si je conçois mes widgets sous forme de directives, existe-t-il un moyen de définir une dépendance à l'intérieur d'une directive ? Par exemple, pour dire que ma directive utilise ng-calender dans son implémentation ?
  • Si je conçois chaque widget comme un module distinct, existe-t-il un moyen d'ajouter dynamiquement le module widget comme une dépendance du module principal ?
  • Comment dois-je concevoir les contrôleurs - un contrôleur par widget probablement ?
  • Comment dois-je séparer l'état (portée) si j'ai plusieurs widgets du même type dans la vue ?
  • Existe-t-il de bonnes pratiques pour concevoir des widgets réutilisables avec AngularJS ?

EDIT

Références utiles :

61voto

joakimbl Points 9081

Ce ne sont que des conseils d'ordre général.

Comment dois-je concevoir mon application et mes widgets ? Dois-je avoir un module AngularJS distinct ou chaque widget doit-il être une directive du module principal ?

Vous parlez de centaines de widgets, il semble naturel de les diviser en plusieurs modules. Certains widgets pourraient avoir plus en commun que d'autres. Certains peuvent être très généraux et s'intégrer dans d'autres projets, d'autres sont plus spécifiques.

Si je conçois mes widgets sous forme de directives, existe-t-il un moyen de définir une dépendance à l'intérieur d'une directive ? Par exemple, pour dire que ma directive utilise ng-calender dans son implémentation ?

Les dépendances vers d'autres modules se font au niveau du module, mais il n'y a pas de problème si le module A dépend du module B et les deux A y B dépend du module C . Les directives sont un choix naturel pour créer des widgets dans Angular. Si une directive dépend d'une autre directive, vous devez soit les définir dans le même module, soit créer la dépendance à un niveau modulaire.

Si je conçois chaque widget comme un module distinct, existe-t-il un moyen d'ajouter dynamiquement le module widget comme une dépendance du module principal ?

Je ne sais pas pourquoi vous voulez faire ça, et je ne sais pas comment le faire. Les directives et les services ne sont pas initialisés avant d'être utilisés dans Angular. Si vous avez une énorme bibliothèque de directives (widgets) et que vous savez que vous utiliserez probablement certaines d'entre elles, mais pas toutes - mais que vous ne savez pas lesquelles seront utilisées lors de l'initialisation de l'application, vous pouvez en fait "charger paresseusement" vos directives après le chargement de votre module. J'ai créé un exemple aquí

L'avantage est que vous pouvez obtenir un chargement rapide de votre application même si vous avez beaucoup de code, car vous n'avez pas besoin de charger les scripts avant d'en avoir besoin. L'inconvénient est qu'il peut y avoir un retard considérable la première fois qu'une nouvelle directive est chargée.

Comment dois-je concevoir les contrôleurs - un contrôleur par widget probablement ?

Un widget aura probablement besoin de son propre contrôleur. Les contrôleurs doivent généralement être de petite taille. S'ils deviennent trop importants, vous pouvez vous demander si une fonctionnalité ne serait pas mieux adaptée à un service.

Comment dois-je séparer l'état (portée) si j'ai plusieurs widgets du même type dans la vue ?

Les widgets qui ont besoin de variables de portée doivent sans aucun doute avoir leurs propres portées isolées ( scope:{ ... } dans la configuration de la directive).

Existe-t-il de bonnes pratiques pour concevoir des widgets réutilisables avec AngularJS ?

Isolez le champ d'application, limitez les dépendances au minimum nécessaire. Voir la vidéo de Misko sur les meilleures pratiques en Angular.

Brian Ford a également écrit un article sur écrire une énorme application en Angular

17voto

beardedlinuxgeek Points 932

Cette question est également très importante pour moi. La page d'accueil d'AngularJS contient quelques exemples (on pourrait les appeler des widgets). J'ai donc parcouru leur code source pour essayer de voir comment ils séparaient leurs widgets.

Premièrement, ils ne déclarent jamais d'attribut "ng-app". Ils utilisent

function bootstrap() {
      if (window.prettyPrint && window.$ && $.fn.popover && angular.bootstrap &&
          hasModule('ngLocal.sk') && hasModule('ngLocal.us') && hasModule('homepage') && hasModule('ngResource')) {
            $(function(){
              angular.bootstrap(document, ['homepage', 'ngLocal.us']);
            });
      }
    }

pour s'assurer que tout est chargé correctement. C'est une bonne idée, mais c'est bizarre qu'ils vous poussent autant à utiliser l'attribut ng-app et qu'ils ne l'utilisent même pas eux-mêmes. Quoi qu'il en soit, voici le module de la page d'accueil chargé avec l'application. http://angularjs.org/js/homepage.js

Il y a une directive appelée appRun

  .directive('appRun', function(fetchCode, $templateCache, $browser) {
    return {
      terminal: true,
      link: function(scope, element, attrs) {
        var modules = [];

        modules.push(function($provide, $locationProvider) {
          $provide.value('$templateCache', {
            get: function(key) {
              var value = $templateCache.get(key);
              if (value) {
                value = value.replace(/\#\//mg, '/');
              }
              return value;
            }
          });
          $provide.value('$anchorScroll', angular.noop);
          $provide.value('$browser', $browser);
          $locationProvider.html5Mode(true);
          $locationProvider.hashPrefix('!');
        });
        if (attrs.module) {
          modules.push(attrs.module);
        }

        element.html(fetchCode(attrs.appRun));
        element.bind('click', function(event) {
          if (event.target.attributes.getNamedItem('ng-click')) {
            event.preventDefault();
          }
        });
        angular.bootstrap(element, modules);
      }
    };
  })

Je vais utiliser la liste des tâches à faire comme exemple. Pour le html, ils ont

<div app-run="todo.html" class="well"></div>

et ensuite en bas de la page, ils ont

<script type="text/ng-template" id="todo.html">
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">
    <span>{{remaining()}} of {{todos.length}} remaining</span>
    [ <a href="" ng-click="archive()">archive</a> ]
    <ul class="unstyled">
      <li ng-repeat="todo in todos">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText"  size="30"
             placeholder="add new todo here">
      <input class="btn-primary" type="submit" value="add">
    </form>
  </div>
</script>

Ils ont également

<style type="text/css" id="todo.css"> //style stuff here </style>
<script id="todo.js"> //controller stuff here </script>

Le code est utilisé, mais les attributs id de ces scripts ne sont pas importants pour le fonctionnement de l'application. C'est juste pour l'affichage du code source à gauche de l'application.

Fondamentalement, ils ont une directive appelée appRun qui utilise une fonction fetchCode

  .factory('fetchCode', function(indent) {
    return function get(id, spaces) {
      return indent(angular.element(document.getElementById(id)).html(), spaces);
    }
  })

pour récupérer le code. Ils utilisent ensuite angular.bootstrap() pour créer une nouvelle application. Ils peuvent également charger des modules via app-run. L'exemple de projet JavaScript est initialisé comme suit

<div app-run="project.html" module="project" class="well"></div>

J'espère que cela vous aidera. Je ne suis toujours pas sûr de la "meilleure" technique, mais il semble que la page d'accueil AngularJS utilise simplement une application angulaire totalement distincte (ng-app) pour chaque exemple/widget. Je pense que je vais faire la même chose, sauf que je vais changer la fonction fetchCode pour obtenir des choses avec AJAX.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X