2017-08-31 10 views
11

J'utilise maintenant des modules css avec des composants AngularJS 1.5. La pile est webpack + css-loader?modules=true + angular 1.5 components + pug.Meilleure façon d'appliquer css-modules à AngularJS

Actuellement, je dois faire les étapes suivantes pour utiliser les modules CSS dans mon modèle pug.

// my-component.js 

import template from 'my-component.pug'; 
import styles from 'my-component.css'; 

class MyComponent { 
    constructor($element) { 
     $element.addClass('myComponent');  // ------ (1) 
     this.styles = styles;     // ------ (2) 
    } 
} 

angular.module(name, deps) 
    .component('my-component', { 
     controller: MyComponent, 
     template: template, 
    }); 

// my-component.pug 
div(class={{ ::$ctrl.styles.fooBar }}) FooBar // ----- (3) 

// my-component.css 
.myComponent { background: green; } 
.fooBar { color: red; } 

Il y a deux problèmes:

  1. Chaque composant doit injecter $element et définir son nom de classe manuellement. La raison pour cela est que le tag de composant AngularJS existe lui-même dans le HTML de résultat sans aucune classe, ce qui rend le CSS difficile. Par exemple, si j'utilise MyComponent ci-dessus comme ceci:

    <div> 
        <my-component></my-component> 
    </div> 
    

    il va générer le code HTML suivant:

    <div> 
        <my-component> 
        <div class="my-component__fooBar__3B2xz">FooBar</div> 
        </my-component> 
    </div> 
    

    Par rapport à ReactJS, <my-component> en HTML résultat ci-dessus est un supplément, parfois il rend CSS difficile écrire. Donc ma solution est (1), pour y ajouter une classe.

  2. La classe dans le modèle est trop longue (3). Je sais que c'est la bonne façon de faire référence à $ctrl.styles.fooBar mais c'est trop long.

Ma solution idéale serait comme ceci:

// my-component.js 
angular.module(name, deps) 
    .component('my-component', { 
     controller: MyComponent, 
     template: template, 
     styles: styles, 
    }); 

// my-component.css 
div(css-class="fooBar") FooBar 

L'idée est de:

  1. font angular.module().component support un styles attribut, qui fera automatiquement (2) this.styles = styles; supplémentaire dans le contrôleur, et appliquer (1) $element.addClass() ainsi.
  2. la directive css-class pour appliquer $ctrl.styles à l'élément.

Ma question est, je n'ai aucune idée de comment mettre en œuvre l'idée 1 ci-dessus (2 est facile). J'apprécie que quelqu'un puisse partager quelque chose à ce sujet. Je suis venu avec une solution que je ne suis pas tout à fait satisfait avec

Répondre

5

Le composant angulaire peut accepter une fonction comme modèle et injecter avec $element. doc

Si le modèle est une fonction, il est injecté avec les sections locales suivantes:

  • $ element - élément actuel
  • $ attrs - attributs actuels objet pour l'élément

Par conséquent, nous pourrions attacher la classe principale pour le composant (.myComponent) dans le modèle amusant ction, puis regex remplace toute occurrence de noms de classes par des noms de classes compilés.

// utils.js 
function decorateTemplate(template, styles, className) { 
    return ['$element', $element => { 
     $element.addClass(styles[className]); 
     return template.replace(/\$\{(\w+)\}/g, (match, p1) => styles[p1]); 
    }]; 
} 

// my-component.js 
import style from './my-component.css'; 
import template from './my-component.pug'; 
import { decorateTemplate } from 'shared/utils'; 

class MyComponent { 
    // NO NEED to inject $element in constructor 
    // constructor($element) { ... 
} 

angular.module(name, deps) 
    .component('myComponent', { 
     // decorate the template with styles 
     template: decorateTemplate(template, styles, 'myComponent'), 
    }); 

// my-component.pug, with special '${className}' notation 
div(class="${fooBar}") FooBar 

Il y a encore un endroit à améliorer que decorateTemplate utilise le remplacement des expressions régulières et le modèle doit utiliser une notation spéciale ${className} pour spécifier les noms de classe de modules css.

Toutes les suggestions sont les bienvenues.

MISE À JOUR

Je mis à jour ma decorateTemplate() fonction pour tirer parti des fonctionnalités de roquet, de sorte que les noms de classes locales peuvent être écrites comme ._localClassName.

// utils.js 
function decorateTemplate(template, styles, className) { 

    return ['$element', ($element) => { 
     $element.addClass(styles[className]); 

     return template.replace(/\sclass="(.+?)"/g, (match, p1) => { 
      let classes = p1.split(/\s+/); 
      classes = classes.map(className => { 
       if (className.startsWith('_')) { 
        let localClassName = className.slice(1); 
        if (styles[localClassName]) { 
         return styles[localClassName]; 
        } else { 
         console.warn(`Warning: local class name ${className} not found`); 
         return className; 
        } 

       } else { 
        return className; 
       } 
      }); 
      return ' class="' + classes.join(' ') + '"'; 
     }); 
    }]; 
} 

// my-component.pug 
._fooBar FooBar 

Bien que ce soit beaucoup plus facile, il ne supprime pas la notation excentrique (commencer par _ pour les noms de classe locale) et regex remplacer.

Toutes les suggestions sont les bienvenues.