How to make Route data typed in Angular 7 (in production)?
Asked Answered
F

3

10
import { environment } from '@env/environment';

export const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: 'organisations',
        component: OrganisationListComponent,
        data: {
          [environment.router.data.resourceName]: 'ORGANISATION' // <- error
        }
      },
      {
        path: 'organisations/create',
        component: OrganisationCreateComponent,
        data: {
          [environment.router.data.resourceName]: 'ORGANISATION_LIST' // <- error
        },...
      }

This is part of one of my routing module files. As you can see I want route data to have a property whose name I defined in environment file. But this won't work when compiling with --aot flag. This is the error:

ERROR in Error during template compile of 'AdminRoutingModule'
  Expression form not supported in 'routes'
    'routes' contains the error at app/admin/admin-routing.module.ts(30,11).

I have around 30 routes in my application, and all of them have data property with the key 'resourceName'. And I don't want to repeat this string 30 times in my application.

I cannot create class which has resourceName property and instantiate it inside data, because function expressions aren't allowed inside route configuration either.

Is there a way around this, or it's simply not possible to achieve with AOT complier?

Edit: this is environement.ts file:

export const environment = {
  production: true,
  version: '1.0.0',
  router: {
    data: {
      resourceName: 'resourceName'
    }
  }
}
Faeroese answered 6/3, 2019 at 19:25 Comment(4)
It just an enum. Replacing ResourceName.ORGANISATION with 'ORGANISATION' doesn't make a differenceFaeroese
Oh indeed, sorry I misread the question (I'll delete my comment).Rah
What if you move your "data" object outside of routes. Something like this: const dat = {}; dat[environment.router.data.resourceName] = 'ORGANISATION'; and then use it inside routes like this: data: datShekinah
still the same error :(Faeroese
R
3

Declare your own Route data type. Extend from @angular/router's Route so it's all type-safe. This way, it's possible to assert the existence of the resourceName property, which could even be omitted otherwise.

No need to involve the environment module in the process.

import { RouterModule, Route } from '@angular/router';

export interface MyRoute extends Route {
    data: {
        resourceName: string;
    };
}

export declare type MyRoutes = MyRoute[];

export const routes: MyRoutes = [
  {
    path: '',
    children: [
      {
        path: 'organisations',
        component: OrganisationListComponent,
        data: {
          resourceName: 'ORGANISATION'
        }
      },
      {
        path: 'organisations/create',
        component: OrganisationCreateComponent,
        data: {
          resourceName: 'ORGANISATION_LIST'
        },...
      }


Rephrase answered 13/3, 2019 at 0:7 Comment(1)
I only had to edit your MyRoute interface so that data is optional, and added children?: MyRoute[] as wellFaeroese
R
1

I understand you tried to narrow down the problem to an easy to reproduce problem but maybe you dumbed it down too much.

This way, from what you can read from the compiler error messages, it's not allowed (expressions fed to the AOT compiler, [propertyName] on objects).

I suggest you avoid this problem completely, as you can have different environment files, just define data like this:

export const environment = {
  production: true,
  version: '1.0.0',
  router: {
    data: {
      'resourceName': 'ORGANISATION', // <- replace 'resourceName' with whatever you need
    }
  }
}

And then use like this:

import { environment } from '@env/environment';

export const routes: Routes = [
  {
    path: '',
    resolve: [TranslateResolver],
    children: [
      {
        path: 'organisations',
        component: OrganisationListComponent,
        canActivate: [RouteGuard],
        data: environment.router.data,
      },...

If your problem at hand requires something else, then again avoid expressions:

export const environment = {
  production: false,
  test: false,
  router: {
    data: {
      resourceName: 'resourceName', // <- replace 'resourceName' with whatever you need
    }
  },
};
export const environment = {
  production: true,
  version: '1.0.0',
  router: {
    data: {
      resourceName: environment.router.data.resourceName,
      value: 'ORGANIZATION',
    }
  }
}

This requires changing the way you consume this data, but hey, it works.

Rephrase answered 8/3, 2019 at 22:19 Comment(3)
The thing is, I have around 30 routes in my application. and all of them have data property with the key resourceName. I added this key to environment file, so I don't have to repeat the same string 30 times. This way I would still repeat it, just in different place.Faeroese
So basically, my main problem is that Route data cannot be typed. I cannot create class which has resourceName property and instantiate it inside data, because function expressions aren't allowed in route config either.Faeroese
I updated the question, it really was dumbed down too muchFaeroese
C
0

using a function, seems to work around this awkward issue.

function mydata() {
    return {[environment.router.data.resourceName]: 'ORGANISATION'};
}

export const routes: Routes = [
  {
    path: '',
    resolve: [TranslateResolver],
    children: [
      {
        path: 'organisations',
        component: OrganisationListComponent,
        canActivate: [RouteGuard],
        data: mydata()
      },...
Colonial answered 6/3, 2019 at 20:49 Comment(3)
compiler says: Function expressions are not supported in decorators in 'routes'Faeroese
please try a simple function, above your export and below your imports. I suspect the issue might be something like this issueColonial
still the same error. I can pass the function to the decorator, but cannot call itFaeroese

© 2022 - 2024 — McMap. All rights reserved.