Dartlang:如何允许第三方使用模拟封装来设置angular2组件的样式?

时间:2017-02-05 20:53:08

标签: angular dart angular-dart

对mods的免责声明:这并没有就最佳方式提出意见,这就要问一个方法,如何解决这个问题。

问题

让我们想象一下,我想为我的项目编写一个材质设计风格按钮组件。 (我知道,这已经可以使用,但这仅用于说明目的。)
我将按钮组件设置为普通的dart库项目,工作正常。现在我想在另一个angular2 app中使用这个材质按钮组件,所以我将它添加到pubspec.yaml并将其作为指令依赖项插入到我的新app-component中。

问题是,如何从外部更改此按钮的悬停颜色?

由于我的组件应该使用样式封装(模拟样式),因此不能简单地全局设置样式。

可能的解决方案

使用自定义CSS属性

这在理论上是好的,然而,它们尚未在所有浏览器中发货,并且没有生产就绪垫片。 为此,有人会在组件内定义自定义css属性并在外部设置它们的值。

使用可调节样式设置的输入属性

只需为我应该更改的样式提供按钮组件输入属性。这确实有效,但仅适用于那些不包含css伪选择器的样式,例如:hover,因为它无法更改:每个javaScript都悬停样式,因此也不是dart。 有人可以在javascript / dart中编写:hover函数来允许这种样式更改,但这看起来很糟糕,因为它只是复制了css引擎中的可用代码。

使用普通的css链接和原生阴影dom

这个适用于本机阴影dom,因为组件内部的样式链接标记由按钮组件的customCssPath属性填充,会将样式正确加载到组件中。 但是,由于shadowdom在所有浏览器中都不是原生的,因此我们无法可靠地使用它。所以我们现在被迫使用模拟封装。

1 个答案:

答案 0 :(得分:8)

解决方案 - 使用SASS(非常感谢Matan Lurey!)

直接询问Angular2 Repo时,Angular2开发者之一Matan Lurey提供了一些关于如何使用Sass mixins进行所需样式操作的指南。

可以在这里找到原始主题: https://github.com/dart-lang/angular2/issues/154

解决方案详情:

但是,由于我发现实施起来并不是很简单,所以这里有一些关于如何实现这项工作的更多细节:

  1. 想象一下,我们有以下文件internal_component.dart

    import 'package:angular2/angular2.dart';
    
    @Component(
      selector: 'internal-component',
      styleUrls: const ['internal_component.css'],
      templateUrl: 'internal_component.html',
    )
    class InternalComponent {}
    
  2. 此组件具有此模板文件internal_component.html

    <div>
      Some Text
    </div>
    
  3. 这个sass样式文件internal_component.sass

    div {
      background-color: #ffff00;
    }
    
    @mixin internal-component-background($selector, $color) {
    
      polyfill-unscoped-rule {
        background-color: $color;
        content: '#{$selector} > div'
      }
    }
    
    1. internal-component纳入其他组件(此处为app_component.dart):

      import 'package:angular2/core.dart';
      import 'package:sass_internal_styles/internal_component/internal_component.dart';
      
      @Component(
        selector: 'my-app',
        styleUrls: const ['app_component.css'],
        templateUrl: 'app_component.html',
        directives: const [InternalComponent],
      )
      class AppComponent {}
      
    2. 模板文件app_component.html内部:

      <internal-component id="comp1"></internal-component>
      <internal-component id="comp2"></internal-component>
      
    3. 最后,在`app_component.scss:

      @import "../lib/internal_component/internal_component.scss";
      @include internal-component-background($selector: '#comp2', $color: red);
      
  4. 会发生什么:

    • 字符串“Some Text”将显示两次,字符串,如internal_component.html中所示,两次显示在app_component.html
    • 第一个字符串将具有黄色背景,如internal_component.scss中所定义。 这可用于定义组件内的默认值!
    • 第二个字符串将具有红色背景,由app_component.scss中包含的mixin定义。

    有一点需要注意:

    为mixin提供的选择器必须出现在使用内部组件的模板内部。在我们的示例中,这是id=comp2

    中的app_component.html属性

    更新1:需要注意的另一件事:

    使用[attribute] css选择器而不是.class选择器,知道css选择器强度hirarchy!

    通过将属性应用于组件的标记来“规范”常规组件样式,并将此组件的样式仅应用于组件选择器以及作用域的属性。由于elem [attribute] -css规则比简单类选择器具有更多的强度,因此类选择器将不起作用。但是,您可以尝试在类之前指定元素名称来修复此问题。

    您还可以将.cl​​ass选择器与!important结合使用。但是,只有在您知道其含义的情况下才能这样做!例如:相同元素的:hover选择器将不再正常工作,因为!important优先。

    为什么会这样:

    polyfill-unscoped-rule块被编译成

    #comp2 > div {
        background-color: red;
    }
    

    app_component.css文件的样式块中。 (因为样式表将通过angular2在仿真模式下移动到标题) 由于样式封装仅是模拟的,因此内部div的子选择器工作正常。

    更新1:polyfill-unscoped-rule块编译由webcomponents.js完成! :)

    有人可能会争辩说,这个伎俩也可以从外面完成,而且没有sass。这是正确的,但是,使用mixin技术,组件的用户不需要深入了解组件的内部结构。他可以使用一些不错的Sass mixins,这应该记录在组件的自述文件中。