Skip to content

Commit 4be049b

Browse files
committed
feat(select): add ability to customize the selected value label
Adds the `md-select-label` directive which allows users to customize the selected value. E.g. it is now possible to do something like this, if the user wanted to reverse the selected label for some reason: ```ts <md-select placeholder="Food" [formControl]="control" #select="mdSelect"> <md-select-label> {{ select.selected?.viewValue.split('').reverse().join('') }} </md-select-label> <md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option> </md-select> ``` Fixes #2275.
1 parent d1abc9e commit 4be049b

9 files changed

+82
-5
lines changed

src/examples/example-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {TabsTemplateLabelExample} from './tabs-template-label/tabs-template-labe
6666
import {RadioOverviewExample} from './radio-overview/radio-overview-example';
6767
import {SidenavOverviewExample} from './sidenav-overview/sidenav-overview-example';
6868
import {SelectOverviewExample} from './select-overview/select-overview-example';
69+
import {SelectLabelExample} from './select-label/select-label-example';
6970
import {ChipsOverviewExample} from './chips-overview/chips-overview-example';
7071
import {ChipsStackedExample} from './chips-stacked/chips-stacked-example';
7172
import {SelectFormExample} from './select-form/select-form-example';
@@ -141,6 +142,7 @@ export const EXAMPLE_COMPONENTS = {
141142
'radio-ng-model': {title: 'Radios with ngModel', component: RadioNgModelExample},
142143
'radio-overview': {title: 'Basic radios', component: RadioOverviewExample},
143144
'select-overview': {title: 'Basic select', component: SelectOverviewExample},
145+
'select-label': {title: 'Select with custom label', component: SelectLabelExample},
144146
'select-form': {title: 'Select in a form', component: SelectFormExample},
145147
'sidenav-fab': {title: 'Sidenav with a FAB', component: SidenavFabExample},
146148
'sidenav-overview': {title: 'Basic sidenav', component: SidenavOverviewExample},
@@ -203,6 +205,7 @@ export const EXAMPLE_LIST = [
203205
RadioOverviewExample,
204206
SidenavFabExample,
205207
SelectOverviewExample,
208+
SelectLabelExample,
206209
SelectFormExample,
207210
SidenavOverviewExample,
208211
SliderConfigurableExample,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/** No CSS for this example */
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<md-select placeholder="Favorite food" #select="mdSelect">
2+
<md-select-label>You have selected: {{ select.selected?.viewValue }}</md-select-label>
3+
<md-option *ngFor="let food of foods" [value]="food.value">
4+
{{ food.viewValue }}
5+
</md-option>
6+
</md-select>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {Component} from '@angular/core';
2+
3+
4+
@Component({
5+
selector: 'select-label-example',
6+
templateUrl: './select-label-example.html',
7+
})
8+
export class SelectLabelExample {
9+
foods = [
10+
{value: 'steak-0', viewValue: 'Steak'},
11+
{value: 'pizza-1', viewValue: 'Pizza'},
12+
{value: 'tacos-2', viewValue: 'Tacos'}
13+
];
14+
}

src/lib/select/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import {NgModule, ModuleWithProviders} from '@angular/core';
22
import {CommonModule} from '@angular/common';
33
import {MdSelect} from './select';
4+
import {MdSelectLabel} from './select-label';
45
import {MdOptionModule} from '../core/option/option';
56
import {
67
CompatibilityModule,
78
OverlayModule,
89
} from '../core';
910
export * from './select';
11+
export * from './select-label';
1012
export {fadeInContent, transformPanel, transformPlaceholder} from './select-animations';
1113

1214

1315
@NgModule({
1416
imports: [CommonModule, OverlayModule, MdOptionModule, CompatibilityModule],
15-
exports: [MdSelect, MdOptionModule, CompatibilityModule],
16-
declarations: [MdSelect],
17+
exports: [MdSelect, MdSelectLabel, MdOptionModule, CompatibilityModule],
18+
declarations: [MdSelect, MdSelectLabel],
1719
})
1820
export class MdSelectModule {
1921
/** @deprecated */

src/lib/select/select-label.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {Directive} from '@angular/core';
2+
3+
/**
4+
* Allows the user to customize the label that is displayed `md-select` has a value.
5+
*/
6+
@Directive({
7+
selector: 'md-select-label, mat-select-label'
8+
})
9+
export class MdSelectLabel { }

src/lib/select/select.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
[style.visibility]="_getPlaceholderVisibility()"
77
[style.width.px]="_selectedValueWidth"> {{ placeholder }} </span>
88
<span class="mat-select-value" *ngIf="selected">
9-
<span class="mat-select-value-text">{{ selected?.viewValue }}</span>
9+
<span class="mat-select-value-text" [ngSwitch]="!!customLabel">
10+
<ng-content select="md-select-label, mat-select-label" *ngSwitchCase="true"></ng-content>
11+
<span *ngSwitchDefault>{{ selected?.viewValue }}</span>
12+
</span>
1013
</span>
1114

1215
<span class="mat-select-arrow"></span>

src/lib/select/select.spec.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ describe('MdSelect', () => {
3939
FloatPlaceholderSelect,
4040
SelectWithErrorSibling,
4141
ThrowsErrorOnInit,
42-
BasicSelectOnPush
42+
BasicSelectOnPush,
43+
SelectWithCustomLabel
4344
],
4445
providers: [
4546
{provide: OverlayContainer, useFactory: () => {
@@ -579,7 +580,6 @@ describe('MdSelect', () => {
579580

580581
});
581582

582-
583583
describe('animations', () => {
584584
let fixture: ComponentFixture<BasicSelect>;
585585
let trigger: HTMLElement;
@@ -1253,6 +1253,19 @@ describe('MdSelect', () => {
12531253
}).toThrowError(new RegExp('Oh no!', 'g'));
12541254
}));
12551255

1256+
it('should allow the user to customize the label', () => {
1257+
const fixture = TestBed.createComponent(SelectWithCustomLabel);
1258+
fixture.detectChanges();
1259+
1260+
fixture.componentInstance.control.setValue('pizza-1');
1261+
fixture.detectChanges();
1262+
1263+
const label = fixture.debugElement.query(By.css('.mat-select-value')).nativeElement;
1264+
1265+
expect(label.textContent).toContain('azziP',
1266+
'Expected the displayed text to be "Pizza" in reverse.');
1267+
});
1268+
12561269
});
12571270

12581271
describe('change event', () => {
@@ -1343,6 +1356,7 @@ describe('MdSelect', () => {
13431356
expect(trigger.textContent).not.toContain('Pizza');
13441357
});
13451358
});
1359+
13461360
});
13471361

13481362

@@ -1589,6 +1603,26 @@ class FloatPlaceholderSelect {
15891603
@ViewChild(MdSelect) select: MdSelect;
15901604
}
15911605

1606+
@Component({
1607+
selector: 'select-with-custom-label',
1608+
template: `
1609+
<md-select placeholder="Food" [formControl]="control" #select="mdSelect">
1610+
<md-select-label>
1611+
{{ select.selected?.viewValue.split('').reverse().join('') }}
1612+
</md-select-label>
1613+
<md-option *ngFor="let food of foods" [value]="food.value">
1614+
{{ food.viewValue }}
1615+
</md-option>
1616+
</md-select>
1617+
`
1618+
})
1619+
class SelectWithCustomLabel {
1620+
foods: any[] = [
1621+
{ value: 'steak-0', viewValue: 'Steak' },
1622+
{ value: 'pizza-1', viewValue: 'Pizza' },
1623+
];
1624+
control = new FormControl();
1625+
}
15921626

15931627
class FakeViewportRuler {
15941628
getViewportRect() {

src/lib/select/select.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AfterContentInit,
33
Component,
4+
ContentChild,
45
ContentChildren,
56
ElementRef,
67
EventEmitter,
@@ -25,6 +26,7 @@ import {ControlValueAccessor, NgControl} from '@angular/forms';
2526
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
2627
import {ConnectedOverlayDirective} from '../core/overlay/overlay-directives';
2728
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
29+
import {MdSelectLabel} from './select-label';
2830
import 'rxjs/add/operator/startWith';
2931

3032

@@ -207,6 +209,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
207209
/** All of the defined select options. */
208210
@ContentChildren(MdOption) options: QueryList<MdOption>;
209211

212+
/** User-supplied override of the selected value label. */
213+
@ContentChild(MdSelectLabel) customLabel: MdSelectLabel;
214+
210215
/** Placeholder to be shown if no value has been selected. */
211216
@Input()
212217
get placeholder() { return this._placeholder; }

0 commit comments

Comments
 (0)