Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 throwing error: Outlet is not activated

I have set up my app so that I have a Recipe Book which has a list of Recipies which when I click on a Recipe it then shows the Recipe Details in a nested route. This then also has a button that when clicked loads the ingredients in a nested route inside the Recipes Details.

So far the routing seems to work, except when I try to navigate to another Recipe. If the Ingredients tray(route) is active then it will change Recipe and collapse the Ingredients Route, If I then try and navigate (without opening the Ingredients) I get the following error:

Uncaught (in promise): Error: Outlet is not activated
Error: Outlet is not activated

It looks like I the router needs Ingredients to be active or else it doesn't understand the nested route. Thats my take on it but not sure how to fix it or when I went wrong.

single-recipe-book-page.component.html

<app-tray class="recipe-list">
    <app-recipe-list [recipe]="recipe"></app-recipe-list>
</app-tray>
<router-outlet></router-outlet>

recipe-detail.component.html

<app-tray class="recipe">
    The routing has worked and loaded recipe.
</app-tray>
<router-outlet></router-outlet>

ingredients-list.component.html

<app-tray class="ingredients-list">
    Recipe Ingredients have loaded.
</app-tray>

app.routes.ts (updated)

export const routerConfig : Route[] = [
  {
    path: 'recipe-books',
    children: [
      {
        path: ':id', component: SingleRecipeBookPageComponent,
        children: [
          {
            path: 'recipes',
            children: [
              { path: ':id', component: RecipeDetailComponent,
                children: [
                  { path: '', component: IngredientsListComponent },
                  { path: 'ingredients', component: IngredientsListComponent }
                ]
              },
              { path: 'new', component: IngredientsListComponent },
              { path: '', component: RecipeDetailComponent }
            ]
          },
          { path: '', redirectTo: 'recipes', pathMatch: 'full' }
        ]
      },
      { path: '', component: RecipeBooksPageComponent }
    ]
  },
  { path: 'ingredients', component: IngredientsComponent },
  { path: '', redirectTo: 'recipe-books', pathMatch: 'full' },
  { path: '**', redirectTo: 'recipe-books', pathMatch: 'full' }
];
like image 498
Daimz Avatar asked Nov 03 '16 23:11

Daimz


3 Answers

This route is invalid and you should get an error for it.

{ path: '' },

A route either needs to have a component, a redirectTo, or children.

If an empty path route has no children it also should have pathMatch: 'full'.

I guess what you want is

{ path: '', component: DummyComponent, pathMatch: 'full' },

Where DummyComponent is a component with an empty view. You can reuse the same DummyComponent for all these paths.

like image 77
Günter Zöchbauer Avatar answered Nov 12 '22 19:11

Günter Zöchbauer


TLDR; answer :

{
      path: ':question',
      runGuardsAndResolvers: GUARDS_RESOLVERS_DISABLED   // blocks component creation
}

Where GUARDS_RESOLVERS_DISABLED is a global function (to avoid issues with AOT).

 export function GUARDS_RESOLVERS_DISABLED() { return false; }

There's a new feature in Angular router that sounds related to this problem, but isn't - as far as I can tell - a direct solution for it. The new option is runGuardsAndResolvers = 'pathParamsChange' and it affects when Angular checks guards and resolvers - and crucially whether it runs this outlet check.

So before I found the new option:

  • I was stepping through into angular code - and the actual error occurs here (highlighted I n red) at context.outlet.component (where component is a getter that throws).

enter image description here

  • So to fix I'd just need to make shouldRun == false somehow.

  • And shouldRun is simply shouldRunGuardsAndResolvers(...) so if that can be made to return false then it won't try to create a component in the 'wrong' place.

So the solution is quite simple:

 {
        path: 'contactus',
        component: ContactUsComponent,

        children: [
            {
                path: 'topics',
                pathMatch: 'full'
            },
            
            {
                path: 'faq',
                component: FaqPanelComponent
            },
            
            { 
                path: 'test',
                component: ContactusTopicListComponent
            },
            {
                path: ':category',
                
                children: 
                [
                    {
                        path: ':question',
                        runGuardsAndResolvers: () => false   // blocks component creation
                    }
                ]
            }
        ]
    },

Currently runGuardsAndResolvers has quite a few options, but the one I'm using just always returns false which is equivalent to never which really ought to be made into an option. But that's how I found out about the new router features :-)

Also if you have two levels you need to put this at both (or possibly just the highest) level:

        {
            path: ':category',
            runGuardsAndResolvers: () => false,
            
            children: 
            [
                {
                    path: ':question',
                    runGuardsAndResolvers: () => false
                }
            ]
        }

Further reading on resolvers: https://blog.angularindepth.com/new-in-angular-v7-1-updates-to-the-router-fd67d526ad05


Edit: To make this work properly with AOT / Angular 9/10+ you can create a global function to avoid the issue mentioned by Sunil.

export function GUARDS_RESOLVERS_DISABLED() { return false; }

Then use this syntax

runGuardsAndResolvers: GUARDS_RESOLVERS_DISABLED
like image 31
Simon_Weaver Avatar answered Nov 12 '22 19:11

Simon_Weaver


This might help someone else.

I was getting this error. I have checked all my defined routes in my route file, the path and components are defined correctly.

The reason of getting this error was that I forgot to add reference of my service in app.module.ts which was being used in one of my component to get data from RestApi.

So, always add service reference in app.module.ts in providers section, immediately after creating service.

imports: [
   BrowserModule,
   HttpModule,
   ......
 ],

providers: [
    AuthService, 
    ........ // Here, service reference
]
like image 3
mmushtaq Avatar answered Nov 12 '22 17:11

mmushtaq