【问题标题】:Dynamically load different css files in angular2 application based on user's language根据用户的语言在 angular2 应用程序中动态加载不同的 css 文件
【发布时间】:2017-01-11 23:16:01
【问题描述】:

我有一个 Angular2 应用程序,它需要支持多种语言。其中一些语言可能是 RTL(即波斯语),而另一些可能是 LTR(即英语)。除了本地化字符串之外,我还需要根据语言的方向来设置组件的样式。

现在using this helper,我可以有两个单独的文件(即 app-rtl.scss 和 app-ltr.scss),它们都导入一个单一的 scss 文件(即 app.scss),这使我可以在其中编写我的 scss 代码一处并自动输出两个文件;每个方向一个。

现在的问题是我需要根据用户的语言以某种方式引用合适的 css 文件。所以如果用户的语言是英语,那么我应该有 <link href="app-ltr.css" rel="stylesheet"> 在标题中,如果是 RTL,<link href="app-rtl.css" rel="stylesheet">。此外,我想将引导程序添加到我的项目中,并且它还有两个不同的 LTR 和 RTL 输出文件。

有没有什么干净的方法来实现这个功能?

我尝试过的:

我创建了两个没有任何模板的虚拟组件(一个用于 LTR,一个用于 RTL),然后使用 styleUrls 分配相应的 scss 文件并设置 encapsulation: ViewEncapsulation.None 使其成为全局样式。然后我在我的根组件模板中使用 *ngIf 初始化它们,并检查语言是否为 LTR。

这将适用于第一个页面加载,因为只有一个组件(假设组件 LTR)处于活动状态,但是一旦您更改语言(即第二个组件 (RTL) 变为活动状态,因此 LTR 被删除),那么与 LTR 关联的样式保留在页面上并且不会被删除。因此,您的页面上同时存在 RTL 和 LTR 样式,这不是预期的。

【问题讨论】:

  • 这绝对可以用 SystemJS 这样的加载器来完成,但是如果你依赖 Angular 的 Ahead of Time 编译,我怀疑你会遇到麻烦。
  • 恕我直言,样式应按组件加载,并在组件被销毁时删除。请查看我更新的评论。

标签: angular angular2-services


【解决方案1】:

您需要有条件地从浏览器中添加或删除样式:

添加

loadIfNot(url,cssId){

     if (!document.getElementById(cssId)){
        var head  = document.getElementsByTagName('head')[0];
        var link  = document.createElement('link');
        link.id   = cssId;
        link.rel  = 'stylesheet';
        link.type = 'text/css';
        link.href = url;
        link.media = 'all';
        head.appendChild(link);
    }else{
        document.getElementById(cssId).disabled = false;///i fit's already there, enable it
    }

  }

用于移除

 disable(cssId){
     document.getElementById(cssId).disabled = true
 }

DisableEnable 是因为浏览器倾向于缓存您的样式,为了使它们启用或禁用,您需要更改该属性

【讨论】:

  • 这是一个完美的解决方案。非常感谢(y)
【解决方案2】:

根据@marbug 的回答,我会尝试使用服务并动态加载和卸载您的“语言”组件。

这将按如下方式工作

import { Injectable, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { LanguageAComponent } from "./language-a.component";
import { LanguageBComponent } from "./language-b.component";
import { Router } from '@angular/router';

@Injectable()
export class LanguageComponentService {
    componentRef: any;

    constructor(private componentFactoryResolver: ComponentFactoryResolver, private router: Router
    ) { }

    loadLanguageA(viewContainerRef: ViewContainerRef) {
        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(LanguageAComponent);
        this.componentRef = viewContainerRef.createComponent(componentFactory);

    }

   loadLanguageB(viewContainerRef: ViewContainerRef) {
        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(LanguageBComponent);
        this.componentRef = viewContainerRef.createComponent(componentFactory);

    }


    unloadComponent() {
        this.componentRef.destroy();
    }

}

【讨论】:

  • 您能否详细说明一下,我将如何将其与 css 文件一起使用
  • 这篇文章是在讨论使用具有不同样式表的虚拟组件,他可以加载和卸载这些组件以便更改样式
【解决方案3】:

也许您可以根据用户语言的方向有条件地向<body> 添加一个类?例如,<body ng-class="{'dir-ltr': ltr, 'dir-rtl': !ltr}">。然后当然你必须将所有的 sass 样式分别包装在 .dir-ltr.dir-rtl 中,以便 DOM 的其余部分使用正确的样式集。这假定在状态加载时已解析语言方向,并且<body> 在您的 Angular 应用程序的范围内。

【讨论】:

    【解决方案4】:

    1) 我建议您使用延迟加载的模块而不是动态 css 文件(请查看official Angular docs 了解更多信息)

    2)我还建议将当前语言放入某些服务,将服务注入适当的组件并使用所需的 css 类。优点:

    2.1) 组件样式不依赖于 body 元素 - 只依赖于服务价值 - 这对于单元测试来说更好/更正确

    2.2) 可能存在使用 LTR 与 RTL 类还不够的用例,而是为特定语言使用一些额外的类(例如,对于英语、德语等语言,表格列可能具有不同的宽度)

    3) 动态加载 css 可能会导致 UI 问题:首先应用初始样式,然后它们“跳转”到“其他”样式(当加载适当的 css 文件时)。所以 1) & 2) 更好(恕我直言)

    2017-06-07 更新(只是为了更清楚)

    app-routing.module 可能是这样的:

    import { NgModule }             from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    export const routes: Routes = [
        { path: '', redirectTo: '/home', pathMatch: 'full'},
        { 
            path: 'home', 
            loadChildren: 'app/my-home-page/my-home-page.module#MyHomePageModule' 
        },
        ...
    ];
    
    @NgModule({
        imports: [RouterModule.forRoot(routes)],
        exports: [RouterModule]
    })
    export class AppRoutingModule {}
    

    my-global.service 可能是这样的:

    @Injectable()
    export class MyGlobalService {
        currentLanguageDirection: string; // i.e. 'ltr' or 'rtl'
        currentLanguage: string;          // i.e. 'en', 'de', 'ru', etc
    }
    

    my-home-page.component.ts 可能是:

    export class MyHomePageComponent {
        ...
    
        constructor(
            private myGlobalService: MyGlobalService, // inject global service
            ...
        ) {
        }
    
        ...
    }
    

    my-home-page.component.html 可以是:

    <div [ngSwitch]="myGlobalService.currentLanguageDirection">
        <my-home-page-rtl *ngSwitchCase="'rtl'"></my-home-page-rtl>
        <my-home-page-ltr *ngSwitchDefault></my-home-page-ltr>
    </div>
    

    即可以使用两个附加组件:my-home-page-ltrmy-home-page-rtl。它们会有单独的 scss|css 文件。

    如果您需要德语的独特实现,那么 my-home-page-ltr.component.html 可以是:

    <div [ngSwitch]="myGlobalService.currentLanguage">
        <my-home-page-ltr-de *ngSwitchCase="'de'"></my-home-page-ltr-de>
        <div *ngSwitchDefault>
            ... (default html code)
        </div>
    </div>
    

    即单独的 my-home-page-ltr-de 组件将具有单独的德语 scss|css。

    附:请注意,您可以通过在@Component 中设置 html 文件来重用它们:

    @Component({
        selector: 'my-home-page...',
        templateUrl: './my-home-page....html', // <-------------- reuse here
        styleUrls: ['./my-home-page....scss']
    })
    

    附言我建议不要使用手动创建/删除组件。 ngSwitchngIf 的使用是更正确的方法(恕我直言)

    P.P.P.S.优点如下。组件及其子组件应由 angular 自动加载/释放。语言切换将执行得非常快,因为当前组件加载了它的子组件 - 您不需要在语言切换期间加载所有组件的样式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-11
      • 2021-08-28
      • 2017-05-17
      • 2013-11-06
      • 2012-02-05
      • 1970-01-01
      • 2020-05-06
      相关资源
      最近更新 更多