Подтвердить что ты не робот

Лучший способ применения css-модулей в AngularJS

Теперь я использую css-модули с компонентами AngularJS 1.5. Стек webpack + css-loader?modules=true + angular 1.5 components + pug.

В настоящее время я должен выполнить следующие шаги, чтобы использовать модули css в шаблоне мопса.

// 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; }

Есть две проблемы:

  • Каждый компонент должен вводить $element и устанавливать его имя класса вручную. Причина этого заключается в том, что сам тег компонента AngularJS существует в результате HTML без каких-либо классов, что делает CSS трудным. Например, если я использую MyComponent выше, например:

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

    он сгенерирует следующий HTML:

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

    По сравнению с ReactJS, <my-component> в приведенном выше результате HTML является дополнительным, иногда он затрудняет запись CSS. Поэтому мое решение (1), чтобы добавить к нему класс.

  • Класс в шаблоне слишком длинный (3). Я знаю, что это правильный способ ссылки $ctrl.styles.fooBar, но это слишком долго.

Мое идеальное решение будет таким:

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

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

Идея состоит в следующем:

  • make angular.module().component поддерживает дополнительный атрибут styles, который автоматически выполнит (2) this.styles = styles; в контроллере и применит (1) $element.addClass().
  • директива css-class применить $ctrl.styles к элементу.

Мой вопрос: я понятия не имею, как реализовать идею 1 выше (2 легко). Я ценю, если бы кто-нибудь мог рассказать об этом.

4b9b3361

Ответ 1

Я придумал решение, с которым я не совсем согласен.

Компонент

Angular может принимать функцию в качестве шаблона и вводить с помощью $element. doc

Если шаблон является функцией, тогда ему вводят следующие локали:

  • $element - Текущий элемент
  • $attrs - Объект текущих атрибутов для элемента

Поэтому мы могли бы прикрепить основной класс для компонента (.myComponent) в функции шаблона, а затем regex заменить все нахождение имен классов на скомпилированные имена классов.

// 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

Есть еще одно место для улучшения, которое decorateTemplate использует замену регулярных выражений, а шаблон должен использовать специальную нотацию ${className} для указания имен классов css-modules.

Любые предложения приветствуются.

UPDATE

Я обновил мою функцию decorateTemplate(), чтобы использовать функции мопса, так что имена локальных классов могут быть записаны как ._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

Хотя это намного проще, он не устраняет причудливую нотацию (начинаем с _ для локальных имен классов) и заменяем regex.

Любые предложения приветствуются.