I have three named router outlets as shown below.
...
<router-outlet name="menus"><router-outlet>
<router-outlet><router-outlet>
<router-outlet name="footer"><router-outlet>
...
In the markup I want to route the first one, menus, to a component with certain submenu junk in it as shown in the docs.
<ul *ngFor="let main of menus;"
routerLink="[{outlets:{menus:[{{main.link}}]}}]"
class="nav-items">{{main.header}}
The error I'm getting says that:
Error: Cannot match any routes. URL Segment: '%5B%7Boutlets:%7Bmenus:%5Bsubmenu1%5D%7D%7D%5D'
Am at a loss what's wrong with the syntax. Googling my fingernails off but haven't found a simple and crude example of a routerLink version showing how to point a route in a named outlet.
Edit: Based on the comments and samples, I need to reformulate the code being used, still with the same error. In the markup:
<ul *ngFor="let main of menus;"
(click)="pullMenu(main.link)"
class="nav-items">{{main.header}}
Then, in TS:
constructor(private router: Router, private route: ActivatedRoute) { }
pullSubmenu(input) {
console.log(input);
this.router.navigate(
[{ outlets: { menus: [input] } }],
{ relativeTo: this.route });
}
Now, I'm getting the following error (submenu1 is the name of configured path).
Error: Cannot match any routes. URL Segment: 'submenu1'
My routing is set up in the module like this.
const routes: Routes = [
{ path: "submenu1", component: Submenu1Component },
{ path: "submenu2", component: Submenu2Component }
];
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
MainAreaComponent,
Submenu1Component,
Submenu2Component
],
imports: [
BrowserModule,
RouterModule.forRoot(routes)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
You need to use the evaluated version [routerLink]:
<ul *ngFor="let main of menus;"
[routerLink]="[{outlets:{menus:[{{main.link}}]}}]"
class="nav-items">{{main.header}}
As an alternative you can emulate the routerLink. Here is the gist of what it does:
#HostListener('click')
onClick(): boolean {
const extras = {
skipLocationChange: attrBoolValue(this.skipLocationChange),
replaceUrl: attrBoolValue(this.replaceUrl),
};
this.router.navigateByUrl(this.urlTree, extras);
return true;
}
So, here is the setup using navigate instead of navigateByUrl:
#Component({
template: `
<ul *ngFor="let main of menus;" (click)="[{outlets:{menus:[{{main.link}}]}}])"
class="nav-items">{{main.header}}
`
...
class MyComponent {
constructor(router: Router, route: ActivatedRoute) {}
navigate(commands) {
this.router.navigate(commands, {relativeTo: this.route})
You can't use unevaluated version of routerLink because it reads commands as a string and if you have outletsin the commands strings don't work. See Navigation to secondary route URL for routerLink attribute to understand why.
Related
I'm trying to get a handle on a simple html <p> tag using the data-cy attribute:
<p data-cy="register">Register</p>
First I mount the component:
it('mounts', () => {
cy.mount(AccountDialogComponent, {
imports: [
HttpClientTestingModule,
ReactiveFormsModule,
AngularMaterialModule,
BrowserAnimationsModule
],
declarations: [ AccountDialogComponent ],
providers: [{provide: AccountService, useValue: accountService}]
})
});
The test I am running for this as follows:
it("text should be 'Register' when user is registering", () => {
cy.get('[data-cy="register"]').should('have.text', 'Register');
});
The test keeps returning a failure:
assertexpected [data-cy="register"] to have text Register, but the
text was ''
After another few seconds, it then returns an Assertion Error:
Timed out retrying after 4000ms: Expected to find element: [data-cy="register"], but never found it.
I can't understand why this isn't working?
I am new to Cypress and this is the first test I am dong since installing Cypress in my Angular app.
I had same problem, this Angular - adding Cypress data-cy attribute sorted it out.
The gist is the Angular framework would not pass on data-cy without it's particular binding,
<p [attr.data-cy]="register">Register</p>
Now, test code will find your element.
BTW Component testing is ok if mount() ok, then it's same syntax for test assertions.
Ok so it seems I was just forgetting that I need to mount the component before each test:
beforeEach(async () => {
cy.mount(AccountDialogComponent, {
imports: [
HttpClientTestingModule,
ReactiveFormsModule,
AngularMaterialModule,
BrowserAnimationsModule
],
declarations: [ AccountDialogComponent ],
providers: [{provide: AccountService, useValue: accountService}]
})
});
I'm trying to make a router for one of my components, but it is not working as expected.
Initially it was working fine, but I had to add another route to decide which mat-tab would be open when redirecting. I added the second route like that, but for some reason the third one stopped working even though the first two were working fine.
import { Routes } from '#angular/router';
import { ActionComponent } from './action.component';
import { ActionResolver } from './action.resolver';
import { ACTION_RESULT_ROUTES } from './result/action-result.routes';
export const ACTION_ROUTES: Routes = [
{ path: ':id', component: ActionComponent, resolve: { action: ActionResolver } },
{ path: ':id/:tab', component: ActionComponent, resolve: { action: ActionResolver } },
{
path: 'action-result',
children: ACTION_RESULT_ROUTES,
},
];
I got a pretty large error when trying the third route, but it starts like this:
ERROR Error: Uncaught (in promise): TypeError: You provided 'null' where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.
Just in case, I tried to reorder it and all three were working fine when I did it like this:
export const ACTION_ROUTES: Routes = [
{ path: ':id', component: ActionComponent, resolve: { action: ActionResolver } },
{
path: 'action-result',
children: ACTION_RESULT_ROUTES,
},
{ path: ':id/:tab', component: ActionComponent, resolve: { action: ActionResolver } },
];
Can anyone tell me why it works like that?
Edit: Added the ACTION_RESULT_ROUTES for clarification
export const ACTION_RESULT_ROUTES: Routes = [
{ path: ':id', component: ActionResultComponent, resolve: { result: ActionResultResolver } },
];
According to Angular:
"The order of routes is important because the Router uses a
first-match wins strategy when matching routes, so more specific
routes should be placed above less specific routes."
It is recommended to have static routes first, therefore your action-result path should go first, followed by the :id/:tab path then :id path last. If you have a wildcard route, it should always be the last route in your array.
The reason behind this logic is that if you had the :id path above the action-result path, angular would use the word 'action-result' as the id in the :id path.
Similarly if you had the :id path above the :id/:tab path, angular would use the words id/tab as the id in the :id path.
So, a rule of thumb is to always put your static routes first, then your dynamic routes from the most specific to the least specific followed by your wildcard route at the end.
E. G.
PATH1
PATH2
PATH3/:USER/:ROLE/:PAGE
PATH4/:SITE/:ID
PATH5:/ID
Wildcard route (*)
Im trying to redirect my page from login to another page. Im following this code.
My login component ts file:
import { Router } from '#angular/router';
constructor(private router: Router) {
}
funLogin(mobilenumber){
this.router.navigateByUrl('registration');
}
In my html Im calling this function in a submit btn,
<button class="common-btn btn" (click)="funLogin(mobileNo.value)">Submit</button>
In my app.login.routing file,
export const loginRouting: Routes = [
{
path: '', component: DashboardRootComponent, canActivateChild: [],
children: [
{ path: '', component: DashboardComponent, pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'registration', component: RegistrationComponent },
]
}
]
I have tried with "this.router.navigate" & referredlot of links. But it didnt work. Can anyone please tell me where Im going wrong or if you could give me a workingh sample it would be better.
#sasi.. try like this,
<a routerLink="/registration"><button class="btn btn-success" > Submit </button></a>
Update :
In order to use the routing in your application, you must register the components which allows the angular router to render the view.
We need register our components in App Module or any Feature Module of it (your current working module) in order to route to specific component view.
We can register components in two ways
.forRoot(appRoutes) for app level component registration like
featuteModules(ex. UserManagement) and components which you want register at root level.
.forChild(featureRoutes) for feature modules child components(Ex. UserDelete, UserUpdate).
you can register something like below,
const appRoutes: Routes = [
{ path: 'user', loadChildren: './user/user.module#UserModule' },
{ path: 'heroes', component: HeroListComponent },
];
#NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(
appRoutes
)
],
P.S : In order to navigate from one component to another, you must include the RouterModule in corresponding Module Imports array from #angular/router package.
You can navigate one to another page in Angular in Two ways. (both are same at wrapper level but the implementation from our side bit diff so.)
routerLink directive
routerLink directive gives you absolute path match like navigateByUrl() of Router class.
<a [routerLink]=['/registration']><button class="btn btn-success" > Submit </button></a>
If you use dynamic values to generate the link, you can pass an array of path segments, followed by the params for each segment.
For instance routerLink=['/team', teamId, 'user', userName, {details: true}] means that we want to generate a link to /team/11/user/bob;details=true.
There are some useful points to be remembered when we are using routerLink.
If the first segment begins with /, the router will look up the route
from the root of the app.
If the first segment begins with ./, or doesn't begin with a slash,
the router will instead look in the children of the current activated
route.
And if the first segment begins with ../, the router will go up one
level.
for more info have look here.. routerLink
Router class
We need inject Router class into the component in order to use it's methods.
There more than two methods to navigate like navigate() , navigateByUrl(), and some other.. but we will mostly use these two.
navigate() :
Navigate based on the provided array of commands and a starting point. If no starting route is provided, the navigation is absolute.
this.route.navigate(['/team/113/user/ganesh']);
navigate() command will append the latest string is append to existing URL. We can also parse the queryParams from this method like below,
this.router.navigate(['/team/'], {
queryParams: { userId: this.userId, userName: this.userName }
});
You can get the these values with ActivatedRoute in navigated Component. you can check here more about paramMap, snapshot(no-observable alternative).
navigateByUrl()
Navigate based on the provided URL, which must be absolute.
this.route.navigateByUrl(['/team/113/user/ganesh']);
navigateByUrl() is similar to changing the location bar directly–we are providing the whole new URL.
I am using angular 7 and I solved it in this way into my project.
1.First We need to implement this Modules to our app.module.ts file
import { AppRoutingModule} from './app-routing.module';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
#NgModule({
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
],
})
2.Then Open your.component.html file and then fire a method for navigate where you want to go
<button class="btn btn-primary" (click)="gotoHome()">Home</button>
3.Then Go your.component.ts file for where you want to navigate. And add this code there.
import { Router } from '#angular/router';
export class YourComponentClassName implements OnInit {
constructor(private router: Router) {}
gotoHome(){
this.router.navigate(['/home']); // define your component where you want to go
}
}
4.And lastly want to say be careful to look after your app-routing.module.ts
where you must have that component path where you want to navigate otherwise it will give you error. For my case.
const routes: Routes = [
{ path:'', component:LoginComponent},
{ path: 'home', component:HomeComponent }, // you must add your component here
{ path: '**', component:PageNotFoundComponent }
];
Thanks I think, I share all of the case for this routing section. Happy Coding !!!
navigateByUrl expects an absolute path, so a leading / might take you to the right page
You could also use navigate and don't need the leading / but the syntax is slightly different as it expects an array for the path
https://angular.io/api/router/Router#navigateByUrl
<a class="nav-link mt-1" [routerLink]="['/login']"><i class="fa fa-sign-in"></i> Login</a>
I have run the command "npm install --save ng2-social-share".
and then add into app.module.ts :-
import { CeiboShare } from 'ng2-social-share';
#NgModule({
imports: [
CeiboShare
]
});
and then i add into my home.component.ts :-
import { CeiboShare } from 'ng2-social-share';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
directives: [CeiboShare]
})
webpack: Compiling...
ERROR in src/app/home/home.component.ts(16,3): error TS2345: Argument of type '{ selector: string; templateUrl: string; styleUrls: string[]; directives: typeof CeiboShare[]; }' is not assignable to parameter of type 'Component'.
Object literal may only specify known properties, and 'directives' does not exist in type 'Component'.
Date: 2018-02-27T09:02:42.288Z - Hash: bedb972b22f9a72ebb59 - Time: 2832ms
5 unchanged chunks
chunk {main} main.bundle.js (main) 367 kB [initial] [rendered]
webpack: Compiled successfully.
The simple way for you is to import in your app.modules.ts like this
1) import {CeiboShare} from 'ng2-social-share';
#NgModule({
declarations: [
AppComponent,
CeiboShare
]
})
2) and then in your home.component.ts
you need to just only define the url sharing link and image url sharing if you want like this:
//vars used only for example, put anything you want :)
public repoUrl = 'https://www.egozola.com';
public imageUrl = 'https://avatars2.githubusercontent.com/u/10674541?v=3&s=200';
the in your home.component.html the you can call sharing easily like this
button ceiboShare [facebook]="{u: repoUrl}">Facebook
Linkedin
Google Plus
Twitter
Pinterest
good all thing is working well
What you could do is to use these functions on your typescript file and call it from the template.
Providing here 5 Social Media Share - Facebook, Pinterest, Twitter, GooglePlus, LinkedIn
// Facebook share won't work if your shareUrl is localhost:port/abc, it should be genuine deployed url
shareOnFacebook(shareUrl: string) {
shareUrl = encodeURIComponent(shareUrl);
window.open(`https://www.facebook.com/sharer/sharer.php?u=${shareUrl}`, 'sharer');
}
shareOnPinterest(shareUrl: string, img: string, desc: string) {
shareUrl = encodeURIComponent(shareUrl);
img = encodeURIComponent(img);
desc = encodeURIComponent(desc);
window.open(`https://www.pinterest.com/pin/create/button?url=${shareUrl}&media=${img}&description=${desc}`, 'sharer');
}
shareOnTwitter(shareUrl: string, desc: string) {
shareUrl = encodeURIComponent(shareUrl);
desc = encodeURIComponent(desc);
window.open(`https://twitter.com/intent/tweet?url=${shareUrl}&text=${desc}`, 'sharer');
}
shareOnGooglePlus(shareUrl: string) {
shareUrl = encodeURIComponent(shareUrl);
window.open(`https://plus.google.com/share?url=${shareUrl}`, 'sharer');
}
// LinkedIn share won't work if your shareUrl is localhost:port/abc, it should be genuine deployed url
shareOnLinkedIn(shareUrl: string, title: string, summary: string) {
shareUrl = encodeURIComponent(shareUrl);
window.open(`https://www.linkedin.com/shareArticle?url=${shareUrl}&title=${title}&summary=${summary}`, 'sharer');
}
Hope this will help you or somebody else.
Thanks!
remove that directives : [CeiboShare] from #component decorator. It is not supported in angular 4 and you don't even need to import it into your component. Just Import into Ngmodule as below,
import { CeiboShare } from 'ng2-social-share';
#NgModule({
imports: [
CeiboShare.forRoot()
]
});
This would suffice to get it working in any component as attribute directives.
When I try to pipe my data by JSON pipe I get the following error in the console:
Unhandled Promise rejection: Template parse errors:
The pipe 'json' could not be found (...
What am I doing wrong?
Most likely you forgot about importing CommonModule:
import { CommonModule } from '#angular/common';
#NgModule({
...
imports: [ CommonModule ]
...
})
As noted in the comments, do this in the module where you're using the json pipe.
In my case CommonModule was added, but the component was not part of declaration of any module
(I was creating component dynamically by using ContainerRef)
Your parent Module of the component should be like this.
import {NgModule} from '#angular/core';
import {AuditTrailFilterComponent} from './components/audit-trail-filter/audit-trail-filter.component';
import {CommonModule} from '#angular/common'; <-- This is important !!!!
#NgModule({
imports: [
CommonModule, <-- This is important !!!!
],
declarations: [AuditTrailFilterComponent],
exports: [
AuditTrailFilterComponent
]
})
export class AuditTrailModule {
}
This can also occur if you declare your component in a Lazy module and try to add the component to a route that hasn't caused that module to be loaded.
eg. I just had this error with:
children: [
{
path: 'editor',
loadChildren: () => from(import(/* webpackChunkName: "editor" */ '../editor/editor.module').then(m => m.EditorModule))
},
{
path: 'multi-preview',
component: MultiPreviewerComponent // declared in Editor.module (lazy loaded)
}
]
For me,
I had to add it on the under the providers in the app.module.ts
#NgModule({
providers: [
{ provide: JsonPipe }, <-- This is important !!!!