Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

How to use in Angular 16 standalone components? #14

Open
AlbertCalvet opened this issue Nov 4, 2023 · 6 comments
Open

How to use in Angular 16 standalone components? #14

AlbertCalvet opened this issue Nov 4, 2023 · 6 comments

Comments

@AlbertCalvet
Copy link

Sorry if this is too basic and not an issue really but I'm quite new to Angular and there's not much documentation about this plugin also.

I'm migrating an existing Angular 9 app to a new Angular 16 seed project that uses standalone components and I'm not sure what should be the setup.

I have a app.config.ts file with the following:

export const appConfig: ApplicationConfig = {
    providers: [
        provideAnimations(),
        provideHttpClient(),

        provideRouter(appRoutes,
            withPreloading(PreloadAllModules),
            withInMemoryScrolling({scrollPositionRestoration: 'enabled'}),
        ),

        // Transloco Config
        provideTransloco(),

        // Transloco router plugin
        {
            provide: LOCALIZE_ROUTER_CONFIG,
            useValue: localizeRouterConfig({
                translateRoute: true
            })
        },

  // some more stuff ...

In app.routes.ts:

    {path: 'sign-in', component: AuthSignInComponent},

And I also added the route translations to es.json file, example:

    "ROUTES": {
        "sign-in": "acceder"
    },

But still if I access http://localhost:4300/acceder I get the following error on the browser console:

ERROR Error: Uncaught (in promise): Error: NG04002: Cannot match any routes. URL Segment: 'acceder'
Error: NG04002: Cannot match any routes. URL Segment: 'acceder'
    at Recognizer.noMatchError (router.mjs:3655:16)
...

I guess I need this glue:

´´´
imports: [
RouterModule.forRoot(routes),
LocalizeRouterModule.forRoot(routes)
],
´´´

But I don't know how to set it up without the @NgModule stuff.

Any help would be very much appreciated.

Thanks!

@penleychan
Copy link
Owner

penleychan commented Nov 4, 2023

That should be enough for basic setup. The url though should be http://localhost:4300/es/acceder where the es is the current language. Can you give that a try?

@AlbertCalvet
Copy link
Author

@penleychan thank you for such a fast reply!

I wasn't aware that I needed to add the language slug in the route, I guess now I'm a little closer to get it working hehe :)

However, http://localhost:4300/es/acceder doesn't seem to work as well, I get the same type of message in the console.

I tried to build a new angular 16 standalone app from scratch just in case this seed template had something interfering, but I can't get it to work there as well.

Any ideas? Thank you so much again.

@penleychan
Copy link
Owner

Sorry, I'll have to get back to you later on this, currently on vacation.

@penleychan
Copy link
Owner

penleychan commented Nov 25, 2023

@AlbertCalvet

Hello, sorry for the delay, I am now back from my break.

I've created an example for ng17 standalone, which should basically be the same as ng16.
https://github.com/penleychan/ngx-transloco-standalone

Looks like you were just missing importProvidersFrom(LocalizeRouterModule.forRoot(routes)), on your providers.

@AlbertCalvet
Copy link
Author

AlbertCalvet commented Dec 8, 2023

Hi!!

First of all thank you so so much for your help, really appreciated. And sorry for coming back so late for feedback.

I tried your suggested change and it worked! ... somehow.

There's a few things that made me struggle, not critical though.

First thing is that this starter kit I'm using defined the available languages for transloco this way:

                availableLangs      : [
                    {
                        id   : 'en',
                        label: 'English',
                    },
                ],

And this made the code break at the getScopeFromLang() method since it expects a string and is getting an object. It seems though that format can be both ways based on the definition in source code:

export declare type AvailableLangs = string[] | LangDefinition[];

I don't know though if this is more related to the transloco plugin or your routing addon?
For the time being, I just changed to a availableLangs : ['en','es'] syntax and it worked, although this would affect the language component to pick the language but that's another issue.

The second thing is that after the first change it still didn't work as expected, it gave me another error:

Uncaught (in promise): Error: ASSERTION ERROR: NgModule '[object Module]' is not a subtype of 'NgModuleType'.

Looking in forums I got to a similar issue hinting that there might be some problem defining the route.
This kit actually makes use of child routes in all the routes, as everything is treated as a module and referred to its own routing file, example:

    // Auth routes for guests
    {
        path: '',
        canActivate: [NoAuthGuard],
        canActivateChild: [NoAuthGuard],
        component: LayoutComponent,
        data: {
            layout: 'empty'
        },
        children: [
            {path: 'confirmation-required', loadChildren: () => import('app/modules/auth/confirmation-required/confirmation-required.routes')},
            {path: 'forgot-password', loadChildren: () => import('app/modules/auth/forgot-password/forgot-password.routes')},
            {path: 'reset-password', loadChildren: () => import('app/modules/auth/reset-password/reset-password.routes')},
            //{path: 'sign-in', loadChildren: () => import('app/modules/auth/sign-in/sign-in.routes')},
            {path: 'sign-up', loadChildren: () => import('app/modules/auth/sign-up/sign-up.routes')}
        ]
    },

    // Test, this works.
    {path: 'sign-in', component: AuthSignInComponent},

However if I directly point to the component skipping the layout component it then works!
Is there something I'm missing to make the routes work with child components or it is not supported?

EDIT: I just checked again and child routes per se also work, the problem seems to be in the loadChildren() stuff for lazing-loading routes.

Finally, this maybe is not a dealbreaker but I observed that the default language routes also need the language slug, or at least they do on my setup, /sign-in no longer resolves the route, I have to use /en/sign-in.
Is this by design? or should I be able to use the original route as well?

Sorry for so many questions! And thank you again.

@penleychan
Copy link
Owner

penleychan commented Dec 9, 2023

On initialization library leverages transloco

setDefaultLang(lang: string): void;
setActiveLang(lang: string): this;

Both method above accepts string, in your case now that getAvailableLangs() returns an object the those methods failed.

Migrating the component that changes locale based on string should be quite straight forward. I would assume now that you don't have to extract the id from the LangDefinition object.

At this moment in time, I do not have any plan to add support for LangDefinition to extract the id from the getAvailableLangs(). However PR is welcome.


As for your routing it seems off:
You mentioned you were using standalone, but in this case you're loading via loadChildren which is used to load modules and your import is incorrect as well, it should look something like import('./child/child.module').then(c => c.ChildModule) or via async: async () => (await import('./child/child.module')).ChildModule

Similarly if you're doing standalone you would use loadComponent and your import should look similar as above: import('./child/child.component').then(c => c.ChildComponent)


Skip Route Localization is supported, you can have a look at the gilsdav documentation on it since this is just a port of the library: https://github.com/gilsdav/ngx-translate-router#excluding-routes

Only thing to note based on your example is that those routes that you don't want to have the lang slug you need to put those routes outside of the root, if you want your child routes to skip lang slug your root need to have skipRouteLocalization since this library set your base route to /:lang/[...]

So your "Auth routes for guests" would look like:

{
        path: '',
        canActivate: [NoAuthGuard],
        canActivateChild: [NoAuthGuard],
        component: LayoutComponent,
        data: {
            layout: 'empty',
            skipRouteLocalization: true
        },
        children: [
            {path: 'confirmation-required', loadChildren: () => import('app/modules/auth/confirmation-required/confirmation-required.routes')},
            {path: 'forgot-password', loadChildren: () => import('app/modules/auth/forgot-password/forgot-password.routes')},
            {path: 'reset-password', loadChildren: () => import('app/modules/auth/reset-password/reset-password.routes')},
            //{path: 'sign-in', loadChildren: () => import('app/modules/auth/sign-in/sign-in.routes')},
            {path: 'sign-up', loadChildren: () => import('app/modules/auth/sign-up/sign-up.routes')}
        ]
    },

Hope that helps

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants