Skip to content

Commit 9115538

Browse files
authored
feat(snackbar): don't require a ViewContainerRef (#1783)
* feat(snackbar): don't require a ViewContainerRef * default config, module method
1 parent f1f660e commit 9115538

File tree

4 files changed

+58
-33
lines changed

4 files changed

+58
-33
lines changed

src/demo-app/snack-bar/snack-bar-demo.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Component, ViewContainerRef} from '@angular/core';
2-
import {MdSnackBar, MdSnackBarConfig} from '@angular/material';
2+
import {MdSnackBar} from '@angular/material';
33

44
@Component({
55
moduleId: module.id,
@@ -16,7 +16,6 @@ export class SnackBarDemo {
1616
public viewContainerRef: ViewContainerRef) { }
1717

1818
open() {
19-
let config = new MdSnackBarConfig(this.viewContainerRef);
20-
this.snackBar.open(this.message, this.action && this.actionButtonLabel, config);
19+
this.snackBar.open(this.message, this.action && this.actionButtonLabel);
2120
}
2221
}

src/lib/snack-bar/snack-bar-config.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import {ViewContainerRef} from '@angular/core';
22
import {AriaLivePoliteness} from '../core';
33

4-
4+
/**
5+
* Configuration used when opening a snack-bar.
6+
*/
57
export class MdSnackBarConfig {
68
/** The politeness level for the MdAriaLiveAnnouncer announcement. */
7-
politeness: AriaLivePoliteness = 'assertive';
9+
politeness?: AriaLivePoliteness = 'assertive';
810

911
/** Message to be announced by the MdAriaLiveAnnouncer */
10-
announcementMessage: string;
12+
announcementMessage?: string = '';
1113

1214
/** The view container to place the overlay for the snack bar into. */
13-
viewContainerRef: ViewContainerRef;
14-
15-
constructor(viewContainerRef: ViewContainerRef) {
16-
this.viewContainerRef = viewContainerRef;
17-
}
15+
viewContainerRef?: ViewContainerRef = null;
1816
}

src/lib/snack-bar/snack-bar.spec.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {inject, async, ComponentFixture, TestBed} from '@angular/core/testing';
22
import {NgModule, Component, Directive, ViewChild, ViewContainerRef} from '@angular/core';
33
import {MdSnackBar, MdSnackBarModule} from './snack-bar';
44
import {OverlayContainer, MdLiveAnnouncer} from '../core';
5-
import {MdSnackBarConfig} from './snack-bar-config';
65
import {SimpleSnackBar} from './simple-snack-bar';
76

87

@@ -50,16 +49,33 @@ describe('MdSnackBar', () => {
5049
});
5150

5251
it('should have the role of alert', () => {
53-
let config = new MdSnackBarConfig(testViewContainerRef);
52+
let config = {viewContainerRef: testViewContainerRef};
5453
snackBar.open(simpleMessage, simpleActionLabel, config);
5554

5655
let containerElement = overlayContainerElement.querySelector('snack-bar-container');
5756
expect(containerElement.getAttribute('role'))
5857
.toBe('alert', 'Expected snack bar container to have role="alert"');
5958
});
6059

60+
it('should open and close a snackbar without a ViewContainerRef', async(() => {
61+
let snackBarRef = snackBar.open('Snack time!', 'CHEW');
62+
viewContainerFixture.detectChanges();
63+
64+
let messageElement = overlayContainerElement.querySelector('.md-simple-snackbar-message');
65+
expect(messageElement.textContent)
66+
.toBe('Snack time!', 'Expected snack bar to show a message without a ViewContainerRef');
67+
68+
snackBarRef.dismiss();
69+
viewContainerFixture.detectChanges();
70+
71+
viewContainerFixture.whenStable().then(() => {
72+
expect(overlayContainerElement.childNodes.length)
73+
.toBe(0, 'Expected snack bar to be dismissed without a ViewContainerRef');
74+
});
75+
}));
76+
6177
it('should open a simple message with a button', () => {
62-
let config = new MdSnackBarConfig(testViewContainerRef);
78+
let config = {viewContainerRef: testViewContainerRef};
6379
let snackBarRef = snackBar.open(simpleMessage, simpleActionLabel, config);
6480

6581
viewContainerFixture.detectChanges();
@@ -84,7 +100,7 @@ describe('MdSnackBar', () => {
84100
});
85101

86102
it('should open a simple message with no button', () => {
87-
let config = new MdSnackBarConfig(testViewContainerRef);
103+
let config = {viewContainerRef: testViewContainerRef};
88104
let snackBarRef = snackBar.open(simpleMessage, null, config);
89105

90106
viewContainerFixture.detectChanges();
@@ -104,7 +120,7 @@ describe('MdSnackBar', () => {
104120
});
105121

106122
it('should dismiss the snack bar and remove itself from the view', async(() => {
107-
let config = new MdSnackBarConfig(testViewContainerRef);
123+
let config = {viewContainerRef: testViewContainerRef};
108124
let dismissObservableCompleted = false;
109125

110126
let snackBarRef = snackBar.open(simpleMessage, null, config);
@@ -127,7 +143,7 @@ describe('MdSnackBar', () => {
127143
}));
128144

129145
it('should open a custom component', () => {
130-
let config = new MdSnackBarConfig(testViewContainerRef);
146+
let config = {viewContainerRef: testViewContainerRef};
131147
let snackBarRef = snackBar.openFromComponent(BurritosNotification, config);
132148

133149
expect(snackBarRef.instance)
@@ -139,7 +155,7 @@ describe('MdSnackBar', () => {
139155
});
140156

141157
it('should set the animation state to visible on entry', () => {
142-
let config = new MdSnackBarConfig(testViewContainerRef);
158+
let config = {viewContainerRef: testViewContainerRef};
143159
let snackBarRef = snackBar.open(simpleMessage, null, config);
144160

145161
viewContainerFixture.detectChanges();
@@ -148,7 +164,7 @@ describe('MdSnackBar', () => {
148164
});
149165

150166
it('should set the animation state to complete on exit', () => {
151-
let config = new MdSnackBarConfig(testViewContainerRef);
167+
let config = {viewContainerRef: testViewContainerRef};
152168
let snackBarRef = snackBar.open(simpleMessage, null, config);
153169
snackBarRef.dismiss();
154170

@@ -159,15 +175,15 @@ describe('MdSnackBar', () => {
159175

160176
it(`should set the old snack bar animation state to complete and the new snack bar animation
161177
state to visible on entry of new snack bar`, async(() => {
162-
let config = new MdSnackBarConfig(testViewContainerRef);
178+
let config = {viewContainerRef: testViewContainerRef};
163179
let snackBarRef = snackBar.open(simpleMessage, null, config);
164180
let dismissObservableCompleted = false;
165181

166182
viewContainerFixture.detectChanges();
167183
expect(snackBarRef.containerInstance.animationState)
168184
.toBe('visible', `Expected the animation state would be 'visible'.`);
169185

170-
let config2 = new MdSnackBarConfig(testViewContainerRef);
186+
let config2 = {viewContainerRef: testViewContainerRef};
171187
let snackBarRef2 = snackBar.open(simpleMessage, null, config2);
172188

173189
viewContainerFixture.detectChanges();
@@ -185,7 +201,7 @@ describe('MdSnackBar', () => {
185201
}));
186202

187203
it('should open a new snackbar after dismissing a previous snackbar', async(() => {
188-
let config = new MdSnackBarConfig(testViewContainerRef);
204+
let config = {viewContainerRef: testViewContainerRef};
189205
let snackBarRef = snackBar.open(simpleMessage, 'DISMISS', config);
190206
viewContainerFixture.detectChanges();
191207

src/lib/snack-bar/snack-bar.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,43 +39,46 @@ export class MdSnackBar {
3939
* Creates and dispatches a snack bar with a custom component for the content, removing any
4040
* currently opened snack bars.
4141
*/
42-
openFromComponent<T>(component: ComponentType<T>,
43-
config: MdSnackBarConfig): MdSnackBarRef<T> {
42+
openFromComponent<T>(component: ComponentType<T>, config?: MdSnackBarConfig): MdSnackBarRef<T> {
43+
config = _applyConfigDefaults(config);
4444
let overlayRef = this._createOverlay();
4545
let snackBarContainer = this._attachSnackBarContainer(overlayRef, config);
46-
let mdSnackBarRef = this._attachSnackbarContent(component, snackBarContainer, overlayRef);
46+
let snackBarRef = this._attachSnackbarContent(component, snackBarContainer, overlayRef);
4747

4848
// When the snackbar is dismissed, clear the reference to it.
49-
mdSnackBarRef.afterDismissed().subscribe(() => {
49+
snackBarRef.afterDismissed().subscribe(() => {
5050
this._snackBarRef = null;
5151
});
5252

5353
// If a snack bar is already in view, dismiss it and enter the new snack bar after exit
5454
// animation is complete.
5555
if (this._snackBarRef) {
5656
this._snackBarRef.afterDismissed().subscribe(() => {
57-
mdSnackBarRef.containerInstance.enter();
57+
snackBarRef.containerInstance.enter();
5858
});
5959
this._snackBarRef.dismiss();
6060
// If no snack bar is in view, enter the new snack bar.
6161
} else {
62-
mdSnackBarRef.containerInstance.enter();
62+
snackBarRef.containerInstance.enter();
6363
}
6464
this._live.announce(config.announcementMessage, config.politeness);
65-
this._snackBarRef = mdSnackBarRef;
65+
this._snackBarRef = snackBarRef;
6666
return this._snackBarRef;
6767
}
6868

6969
/**
70-
* Creates and dispatches a snack bar.
70+
* Opens a snackbar with a message and an optional action.
71+
* @param message The message to show in the snackbar.
72+
* @param action The label for the snackbar action.
73+
* @param config Additional configuration options for the snackbar.
74+
* @returns {MdSnackBarRef<SimpleSnackBar>}
7175
*/
72-
open(message: string, actionLabel: string,
73-
config: MdSnackBarConfig): MdSnackBarRef<SimpleSnackBar> {
76+
open(message: string, action = '', config: MdSnackBarConfig = {}): MdSnackBarRef<SimpleSnackBar> {
7477
config.announcementMessage = message;
7578
let simpleSnackBarRef = this.openFromComponent(SimpleSnackBar, config);
7679
simpleSnackBarRef.instance.snackBarRef = simpleSnackBarRef;
7780
simpleSnackBarRef.instance.message = message;
78-
simpleSnackBarRef.instance.action = actionLabel;
81+
simpleSnackBarRef.instance.action = action;
7982
return simpleSnackBarRef;
8083
}
8184

@@ -115,6 +118,15 @@ export class MdSnackBar {
115118
}
116119
}
117120

121+
/**
122+
* Applies default options to the snackbar config.
123+
* @param config The configuration to which the defaults will be applied.
124+
* @returns The new configuration object with defaults applied.
125+
*/
126+
function _applyConfigDefaults(config: MdSnackBarConfig): MdSnackBarConfig {
127+
return Object.assign(new MdSnackBarConfig(), config);
128+
}
129+
118130

119131
@NgModule({
120132
imports: [OverlayModule, PortalModule, CommonModule],

0 commit comments

Comments
 (0)