-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Sidenav] Open sidenav from another component #2936
Comments
Not sure about the Z-Indexing without seeing your code but my guess is they are on different z-planes. To trigger you can call the sidenav explicitly (which I'm guessing you're doing) like this: <md-sidenav-container class="example-container">
<md-sidenav #sidenav class="example-sidenav">
Jolly good!
</md-sidenav>
<div class="example-sidenav-content">
<button md-button (click)="sidenav.open()">
Open sidenav
</button>
</div>
</md-sidenav-container> BUT You can also use that selector and the then you can fire |
@DennisSmolek Hi there, could you be a bit more precise on the usage on @ViewChild() ? So should I add a viewChild decorator to my app component and to my home component? Thanks |
I guess he's got the following structure.
The "problem" here is that md-sidenav-container captures md-sidenav to ng-content. Hence his custom-sidenav ending up in md-sidenav-content messing up the backdrop. A possible "fix" would be to change to select="md-sidenav, [md-sidenav]". This way we could create sidenav components with the attribute md-sidenav that contains the actual sidenav. |
I'm got a similar problem, though not deeply nested
|
So a local variable allows you to attach to the component and issue commands to it but your sidenav is actually nested within ANOTHER component. right? What I would do is use So: template: '<a (click)="navOpen()">Toggle Da Nav!</a>'
export class HeaderComponent {
@Output() navToggle = new EventEmitter<boolean>();
navOpen() {
this.navToggle.emit(true);
}
} Then in <md-sidenav-container>
<md-sidenav #mainNav>
<!-- sidenav content -->
</md-sidenav>
<!-- primary content -->
</md-sidenav-container>
<headerComponent (navToggle)="mainNav.toggle()"></headerComponent> Or if you want to have more drastic communication between components I prefer the service method template: '<a (click)="toggleNav()">Toggle Da Nav!</a>'
export class HeaderComponent {
constructor(public mySuperService:MySuperService) {
}
toggleNav() {
this.mySuperService.sidenav.toggle();
}
} Then in <md-sidenav-container>
<md-sidenav #mainNav>
<!-- sidenav content -->
</md-sidenav>
<!-- primary content -->
</md-sidenav-container>
<headerComponent></headerComponent>
`mainComponent.ts`
@ViewChild('mainNav') public mainNav;
constructor(public mySuperService: MySuperService) {
this.mySuperService.sidenav = this.mainNav;
}
`mySuperService`
@Injectable()
export class MySuperService {
public sidenav: any;
} There is some great reading here in the ngx on |
@DennisSmolek I'm a bit of a rookie here when it comes to constructors and ngOnInit(), I was getting an error until I added the "this.mySuperService.sidenav = this.mainNav" code into my ngOnInit instead of my constructor. Does ngOnInit() override predefined values that have been set in the constructor or something? |
No, The part that's confusing you I think is with visibility in the constructor. When you define the visibility you remove the need to declare it before the constructor. export class MyComponent {
// vars
private _MySuperService: MySuperService;
constructor(mySuperService: MySuperService) {
// without declaring in the constructor, mySuperService is only local, like this variable
let localOnly = 'whatever';
this._mySuperService = mySuperService;
}
otherFunction() {
console.log(this._mySuperService.whatever);
}
} So thats a pain, Typescript lets you do this instead export class MyComponent {
constructor(private _mySuperService: MySuperService) {}
otherFunction() {
console.log(this._mySuperService.whatever);
}
} But if you do that, remember you've already declared it bound to constructor(private _myService: MyService) {
// THIS WILL FAIL AND THROW ERRORS
let whatever = _myService.whatever;
//This wont
let whateverTwo = this._myService.whatever;
} } |
Hmm, and if I have following structure on my app.component.html <md-sidenav-container fxFlex fxFlexFill>
<md-sidenav #sidenav>
Drawer content
</md-sidenav>
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxFlexFill>
<header>
<router-outlet name="header" ></router-outlet>
</header>
<article fxFlex>
<router-outlet></router-outlet>
</article>
<footer>
<router-outlet name="footer"></router-outlet>
</footer>
</div>
</md-sidenav-container> And I want to toggle that sidenav on any component that is rendered inside header router-outlet? |
@tarlepp A service would work. The catch is you need to assign the element in ngAfterViewInit(). I just spent about an hour trying to figure out why nothing worked. |
@flamusdiu yeah got that working on my simple app almost like that with following setup: app.component.html I hope this helps others too |
@tarlepp thanks a lot! |
Another one solution for discussion.
Actually i implemented this functionality with I wanted to pass One thing what i don't like is that |
In my case i have structure
And few more forms, idea is that as soon as i drom FormComponent to any place it has everything i need. Now it means that md-sidenav must me child of md-sidenav-container |
Hi, When I implement @flamusdiu solution, I am getting undefined for sidenav in this.sidenav.toggle(isOpen) of sidenav.service.ts Despite setting the sidenav in app component it's undefined when clicking the burger menu from header component. I believe the issue is with the below line. If we declare in the following way this keywork works but unable to find toggle() function in it. |
Hi guys, Here is an example of how to pass the MdSidenav reference from a parent component to its children using Parent component HTML: <md-sidenav-container style="height: 100%; width: auto">
<md-sidenav #searchSidenav style="width: 500px;"> <!-- create the reference to the sidenav -->
<my-search [sidenavRef]="searchSidenav"></my-search> <!-- pass the reference to the 'sidenavRef' input of the child element -->
</md-sidenav>
<div class="row" style="height: 100%">
<div class="col-xs-1 col-md-1 col-lg-1 left-column">
...
</div>
<div class="col-xs-11 col-md-11 col-lg-11">
<router-outlet></router-outlet>
</div>
</div>
</md-sidenav-container> SearchComponent HTML: <div class="col">
<div class="row">
...
</div>
<div class="row">
<div class="col">
<button md-mini-fab
(click)="sidenavRef.close()"> <!-- use the 'sidenavRef' variable inside the SearchComponent.ts -->
<md-icon>arrow_back</md-icon>
</button>
</div>
<div class="col">
<form [formGroup]="searchForm">
<md-input-container floatPlaceholder="never"
style="width:100%">
<input mdInput
formControlName="search"
placeholder="{{'HEADER.SEARCH' | translate}}">
</md-input-container>
</form>
</div>
</div>
</div> SearchComponent.ts: export class SearchComponent {
@Input() private sidenavRef: MdSidenav;
...
} The SearchComponent has a variable that holds the reference to the MdSidenav from the parent. It gets the reference through the |
I get a I'm using the following code: The Layout Service
The layout component
The right menu html and component
In the console I get an |
The If the |
In my opinion the sidenav is hard to use from external components. Md-sidenav-container is a weird strategy. Why is the side-nav not with a fixed/absolute property or something but with a parent container? Am I missing something cool? Also the opened property does not work as expected. Here is a good implementation which you can stick anywhere without any need of parent components. https://material-ui-1dab0.firebaseapp.com/demos/drawers This can actually be hooked to a state management system:
Wanted outcome: app.component.html: NOT: app-topbar></app-topbar sidenav-container> |
Step 01:In your Sub component,put the custom event as below, <button mat-raised-button color="accent" (click)="navOpen()"> Step 02:Change your typescript file as fallows, import {Component} from '@angular/core'; export class SubComponent{
} Step 03: In your main component you can put , <sub-component (navToggle)="sidenav.open()"> (NOTE:Don't forget to add "#sidenav" to your mat-sidenav .) |
True, true. And it could be done through a service. A service can implement global state through Observable, Subject, BehaviorSubject or redux etc. I guess the missing piece would be the onClose callback. |
How did you know the type was EDIT:actually, I am getting |
@dman777 Yes.You need to read the API documentation.My above answer related to angular material 2.I think you are using angular 1.x. |
@walakulu No, I am using Angular 5 and Angular Material 2. I can grab the element fine if I don't give it the export class AppComponent implements AfterViewInit {
@ViewChild('snav') public snav; |
@dman777 It's a bad idea to mix Angular 5 and Angular Material 2 together. Either downgrade your Angular version or upgrade your Angular version. The reason why you can't get the import { MdSidenav } from '@angular/material'; |
@Chan4077 Thank you for the tip, but Since Angular 5 is stable and production ready, I am pretty sure Angular Material supports it. @walakulu I looked at the documentation and it saids:
so, I tried: export class AppComponent implements AfterViewInit {
@ViewChild('snav') public snav: matSidenav; but got Again, it works if I do not specify a type on |
Hi @dman777, you need to import correctly, I recommend you use the import of your code editor, in my case I use Visual Studio Code. Your typescript file import { MatSidenav } from '@angular/material';
export class AppComponent implements AfterViewInit {
@ViewChild('snav') public snav: MatSidenav; your template <mat-sidenav #snav>
</mat-sidenav>
|
That did it, thanks! |
Closing this since I believe it is obsolete. |
And how I should unit test this component that has a sidenav as @input location-toolbar.component.tsimport { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { MatSidenav } from '@angular/material';
@Component({
selector: 'app-location-toolbar',
templateUrl: './location-toolbar.component.html',
styleUrls: ['./location-toolbar.component.scss']
})
export class LocationToolbarComponent implements OnInit {
// Input
@Input() sidenav: MatSidenav;
@Input() hideButtons?: boolean;
// Output
@Output() onchange: EventEmitter<any> = new EventEmitter();
constructor() { this.hideButtons = false; }
ngOnInit() {}
} location-toolbar.component.spec.tsimport { async, ComponentFixture, TestBed } from '@angular/core/testing';
// Modules
import { MaterialModule } from '@app-global-modules/material.module';
// Components
import { LocationToolbarComponent } from './location-toolbar.component';
describe('LocationToolbarComponent', () => {
let component: LocationToolbarComponent;
let fixture: ComponentFixture<LocationToolbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MaterialModule],
declarations: [ LocationToolbarComponent ]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(LocationToolbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
//
component.sidenav = null;
component.hideButtons = false;
component.ngOnInit();
await fixture.whenStable();
fixture.detectChanges();
});
fit('should create', () => {
expect(component).toBeTruthy();
});
}); |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Is it possible to have sidenav in separated component?
I have one component where is some content and button which if I click it opens the sidenav that is in another component (just sidenav is there). I tried that to implement with event emitter but not successfuly and I see that when sidenav is opened, it is showing under the layer of my content in first component. Maybe it is because the content is not in md-sidenav-content? I tried to set z-index but nothing happend. Is it possible? thanks
I need to have sidenav code separated in another component because it is my bachelors thesis and I need to have nice code. Thanks
The text was updated successfully, but these errors were encountered: