Angular 5 - 在routerLink中使用ngx-translate

4
在我的项目中(Angular 5 + Firebase),用户需要在登录页面选择语言。为此,我添加了选择选项,并将所选值作为参数传递到第一页,如下所示:
form_login(f:NgForm){
    if (!f.valid)
      return;
    this.afAuth.auth.signInWithEmailAndPassword(f.controls.email.value, f.controls.password.value)
      .then(ok => {     
        this.router.navigate(["/perfil", f.controls.lang.value]); //>> here
      });
  }

在url中有这个参数时,我会检索该值并使用ngx-translate翻译整个页面,就像这样:

perfil.component.ts

import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';

constructor(private translate: TranslateService,  
    private route: ActivatedRoute, 
    private router: Router) { 

      ...
      translate.setDefaultLang('en');

      let lang = this.route.snapshot.paramMap.get('lang');
      this.translate.use(lang);
      console.log(lang);
    }

perfil.component.html

<h5 translate>Companyprofile</h5>

它完美地运作。但是由于也有导航栏,这个组件根本没有获取到语言值。虽然翻译了链接标签,但每个routerLink的值都无法捕获参数的值,相反,它将每个链接设置为undefined,而不是语言的值。

navbar.component.ts

import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';

constructor(private translate: TranslateService,  
    private route: ActivatedRoute, 
    private router: Router) { 

      ...
      translate.setDefaultLang('en');

      let lang = this.route.snapshot.paramMap.get('lang');
      this.translate.use(lang);
      console.log(lang); // >> in this case, the console displays `null`
    }

尝试在navbar.component.ts获取此值时,我在控制台上看到了以下错误:

navbar.component.ts:36 null
zone.js:2935 GET http://localhost:4200/assets/i18n/null.json 404 (Not Found)

core.js:1440 ERROR HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: "Not Found", url: "http://localhost:4200/assets/i18n/null.json", ok: false, …}

navbar.component.html

 <header id="topnav">
   <ul class="navbar-nav" *ngFor="let p of perfil | async">
     <li class="nav-item">
       <a [routerLink]="['/agenda/', lang]" class="nav-link" translate>Agenda</a>
     </li>
     <li class="nav-item">
       <a [routerLink]="['/admin/', lang]" class="nav-link" translate>Admin</a>
     </li>
   ...
   </ul>
 </header>
 <router-outlet></router-outlet> <!-- here I call other components, e.g perfil.component.html

lang参数应该传入值'en'。但是,实际上它的值是undefined

编辑:我所有的组件都是NavbarComponent的子级。 NavbarComponent没有path,因此无法像其他路径一样设置参数。

app.routing.module.ts

const AppRoutes: Routes = [

    {path: '', component: AppComponent, children: [
        { path: '', redirectTo: '/login', pathMatch: 'full' },
        {path: '', component: NavbarComponent, children: [
            {path: 'agenda/:lang', component: AgendaComponent},
            {path: 'perfil/:lang', component: PerfilComponent},
            {path: 'servicos/:lang', component: CadastroServicoComponent},
            {path: 'profissionais/:lang', component: ProfissionaisComponent},
            {path: 'admin/:lang', component: AdminComponent},
            {path: 'dashboard/:lang', component: DashboardComponent},
            {path: 'signup/:lang', component: SignUpFormComponent}
        ]}
    ]}
]    

@NgModule({
imports: [RouterModule.forRoot(AppRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

代码有什么问题?

你正在使用 lang 变量在 navbar.component.html 中创建 routerLink,如果我看得正确的话。你是否在 navbar.component.ts 文件中设置了这个变量?因为在你的代码中,我只能在 perfil.component.ts 文件中找到 lang 变量。 - Benedikt Schmidt
是的,我也在 navbar.component.ts 中使用了它。我已经编辑了我的问题,并在那种情况下添加了关于 console.log 的注释。谢谢! - Atoyansk
1个回答

0

好的,根据我从这段代码中能看到的,我有一个建议。

您的导航栏可能作为一个子组件插入到您的其中一个组件中,就像在您的perfil.component.html文件中,可能会像这样子。

<navbar [parameters...]></navbar>

我不是100%确定,但我猜测ActivatedRoute注入只适用于通过直接路由器链接加载的组件,而不适用于子模块(我真的不确定)。这意味着AcitvatedRoute适用于perfil.component但不适用于navbar.component(因为未从您的路由器调用)。但是,如果您的导航栏像上面显示的那样被调用,您可以将lang变量作为输入参数从perfil.component发送到导航栏。

这是perfil.component.html

<navbar [lang]="lang"></navbar>

这是 `navbar.component.ts` 文件:
// the lang string is now an input parameter
@Input() lang: string;

constructor(private translate: TranslateService, private router: Router) { 
  ...
  // the default language should already been set in the parent component
  // NO NEED: translate.setDefaultLang('en');
}

// the input parameter are not yet availbable in the constructor, but on init
ngOnInit(){
  this.translate.use(lang);
  console.log(lang);
}

希望这能有所帮助。

编辑

在看到你的命令后,我明白了导航栏出错的原因。你的导航栏组件是第一个被调用的,可以说是父组件,在子组件初始化之前构建。

问题在于导航栏有自己的当前路由。你试图在父组件中访问子路由。我找到了一个类似的问题here,有一个被接受的答案,但似乎不再起作用。如果需要的话,你可以查看一下,还有另外一个解决方案,虽然看起来有些棘手。所以我不确定这是否是正确的方法。

改进建议

我看到的问题是使用语言作为组件的路由参数,而不是组件的路由参数。语言目前是每个导航栏子路由都设置的路由参数,导致代码中有很多重复。所有子组件无论如何都必须经过导航栏的初始化过程,那为什么不在那里处理语言相关的内容呢?这样你只需要在代码中的一个地方进行语言加载。

如果是这种情况,您可能需要调整路由器设置,类似这样。

    { path: '', redirectTo: '/login', pathMatch: 'full' },
    { path: ':lang', component: NavbarComponent, children: [
        {path: 'agenda', component: AgendaComponent},
        {path: 'perfil', component: PerfilComponent},
        {path: 'servicos', component: CadastroServicoComponent},
        {path: 'profissionais', component: ProfissionaisComponent},
        {path: 'admin', component: AdminComponent},
        {path: 'dashboard', component: DashboardComponent},
        {path: 'signup', component: SignUpFormComponent}
    ]}

同时,目前未能正常工作的代码也不再需要,因为您不必再为子路由指定语言。

<header id="topnav">
   <ul class="navbar-nav" *ngFor="let p of perfil | async">
     <li class="nav-item">
       <!-- without / the router routes to children relative to the current path -->
       <!-- no need for the language anymore -->
       <a [routerLink]="['agenda']" class="nav-link" translate>Agenda</a>
     </li>
     <li class="nav-item">
       <a [routerLink]="['admin']" class="nav-link" translate>Admin</a>
     </li>
   ...
   </ul>
 </header>

从您的登录页面,直接路由到/lang页面(即导航栏),然后在那里初始化语言(仅执行一次)。所有子组件现在都是lang路由的子级,而不是拥有自己的lang参数。在navbar.component.ts中,您可能可以使用与当前使用的完全相同的代码。在子组件中,不再初始化ngx-translate,它现在集中在父组件中。如果您想在其中一个子组件中使用语言参数,仍然可以访问当前路由,因为该参数仍然是路由的一部分,只是向左移动了一点。
希望这足够准确。

感谢您的回答。实际上,情况恰恰相反。我已经重新编辑了 navbar.component.html 代码,这样您就可以看到其他组件是在导航栏下面加载的。如果您查看路由代码,在 app.routing.module.ts 中,您会注意到 PerfilComponentNavbarComponent 的子级,并且后者没有路径,因此无法在路由配置中包含参数。 - Atoyansk
我已经更新了答案,提供了一个新的解决方案。我个人认为整个问题出现在设计上,应该很快就能调整。只需将“lang”参数从所有子组件切换到父组件,你就可以顺利进行了。 - Benedikt Schmidt
在你的更新之后,我尝试按照你的建议操作。但是,不幸的是,它们还没有奏效。现在,第一页不再是“登录”页面,而是“导航栏”(下面没有任何内容),但URL仍为http://localhost:4200/login。此外,现在链接是使用登录作为其父路由构建的,例如http://localhost:4200/login/agenda - Atoyansk
如果我理解正确的话,您有一个登录页面,该页面位于正常页面之前,因此您将初始调用重定向到您的登录页面。所以第一页应该没问题。从那里开始,在登录本身之后(在其中选择语言),您必须进行内部重定向(例如,在登录操作中调用路由器),以导航栏链接,当然,这仅在您从登录页面获取语言参数时才有效。您的路由器中没有登录路由,您是否已经完成了选择语言的登录? - Benedikt Schmidt
是的,有登录路由({ path: '', redirectTo: '/login', pathMatch: 'full' },使得每当导航到“/”时打开登录页面)和登录页面(请参见我发布的第一个代码片段,在那里我验证用户并检索他们选择的语言)。 - Atoyansk
我猜应该有一个登录页面的路由,类似于{path: 'login', component: LoginComponent},否则路由器会认为“login”是语言参数,并将其路由到“:lang”路由。你必须确保在“:lang”路由之前定义“login”路由,它将选择第一个匹配的路由。 - Benedikt Schmidt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接