<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>angular playground</title>
<link rel="stylesheet" href="style.css" />
<link href="https://unpkg.com/@angular/material/prebuilt-themes/indigo-pink.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>body { font-family: Roboto, Arial, sans-serif; }</style>
<script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js/dist/zone.js"></script>
<script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<my-app>
loading...
</my-app>
</body>
</html>
/* Styles go here */
body {
background: #f1f1f1;
margin: 0;
}
.title {
color: #fff;
margin-right: 15px;
}
.toolbar.mat-toolbar.mat-primary {
background: #334658;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.24);
}
.toolbar .mat-button-focus-overlay {
background: rgba(255,255,255,.12);
}
.example-card {
margin: 20px 20%;
}
.example-card .mat-card-content {
font-size: 16px;
}
/* Menu styles */
.sub-menu {
margin-top: -48px;
}
.sub-menu.mat-menu-panel.mat-menu-before.mat-menu-below {
transform-origin: left top !important;
}
.custom-menu.mat-menu-panel {
min-width: 180px;
}
top-menu [md-button] {
z-index: 1001;
line-height: 64px;
color: #fff;
}
[md-menu-item].mat-menu-item {
display: flex;
align-items: center;
justify-content: space-between;
}
[md-menu-item].mat-menu-item span:first-child {
margin-right: 15px;
}
[md-menu-item].mat-menu-item .mat-icon {
margin-right: 0;
}
.mat-divider {
border-top: 1px solid rgba(0,0,0,.12);
margin: 0;
}
.cdk-overlay-backdrop {
display: block;
}
.cdk-overlay-backdrop ~ .cdk-overlay-backdrop {
display: none;
}
/* End Menu styles */
### Angular Starter Plunker - Typescript
var materialVersion = '@2.0.0-beta.11';
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
paths: {
'npm:': 'https://unpkg.com/'
},
//map tells the System loader where to look for things
map: {
'app': './src',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
'@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
'@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
'@angular/cdk': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk.umd.js',
'@angular/cdk/a11y': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-a11y.umd.js',
'@angular/cdk/bidi': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-bidi.umd.js',
'@angular/cdk/coercion': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-coercion.umd.js',
'@angular/cdk/collections': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-collections.umd.js',
'@angular/cdk/keycodes': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-keycodes.umd.js',
'@angular/cdk/observers': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-observers.umd.js',
'@angular/cdk/overlay': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-overlay.umd.js',
'@angular/cdk/platform': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-platform.umd.js',
'@angular/cdk/portal': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-portal.umd.js',
'@angular/cdk/rxjs': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-rxjs.umd.js',
'@angular/cdk/scrolling': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-scrolling.umd.js',
'@angular/cdk/table': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-table.umd.js',
'@angular/material': 'npm:@angular/material@2.0.0-beta.11/bundles/material.umd.js',
'@angular/cdk/stepper': 'https://unpkg.com/@angular/cdk' + materialVersion + '/bundles/cdk-stepper.umd.js',
'rxjs': 'npm:rxjs',
'typescript': 'npm:typescript@2.2.1/lib/typescript.js'
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
}
}
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';
platformBrowserDynamic().bootstrapModule(AppModule)
//our root app component
import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { MdButtonModule, MdCardModule, MdIconModule, MdMenuModule, MdToolbarModule } from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TopMenuComponent } from './top-menu.component';
@Component({
selector: 'my-app',
templateUrl: 'src/app.html',
})
export class App {
menuItems = [
{
text: 'File',
items: [
{
text: 'Share'
},
{
text: 'New',
icon: 'arrow_right',
items: [
{
text: 'Document'
},
{
text: 'New',
icon: 'arrow_right',
items: [
{
text: 'New',
icon: 'arrow_right',
items: [
{
text: 'Document'
},
{
text: 'Spreadsheet'
}
]
},
{
text: 'Document',
icon: 'arrow_right',
items: [
{
text: 'Document 1'
},
{
text: 'Document 2'
}
]
}
]
},
{
text: 'Spreadsheet'
},
{
text: 'Presentation',
icon: 'arrow_right',
items: [
{
text: 'New',
icon: 'arrow_right',
items: [
{
text: 'Document'
},
{
text: 'Spreadsheet'
}
]
}
]
},
{
text: 'Form'
}
]
},
{
divider: true
},
{
text: 'Open',
extraText: 'Ctrl+O'
},
{
text: 'Rename',
disabled: true
},
{
divider: true
},
{
text: 'Print',
extraText: 'Ctrl+P'
}
]
},
{
text: 'Edit',
items: [
{
text: 'Undo',
extraText: 'Ctrl+Z'
},
{
text: 'Redo',
extraText: 'Ctrl+Y'
},
{
divider: true
},
{
text: 'Cut',
extraText: 'Ctrl+X'
},
{
text: 'Copy',
extraText: 'Ctrl+C'
},
{
text: 'Paste',
extraText: 'Ctrl+P'
},
{
divider: true
},
{
text: 'Find and replace...',
extraText: 'Ctrl+Shift+H'
}
]
},
{
text: 'View',
items: [
{
text: 'Print layout'
},
{
text: 'Mode',
icon: 'arrow_right',
items: [
{
text: 'Presentation'
},
{
text: 'Edit'
},
{
text: 'Modifiable'
}
]
},
{
divider: true
},
{
text: 'Show ruler'
},
{
text: 'Show equation toolbar'
},
{
text: 'Show spelling suggestions'
},
{
divider: true
},
{
text: 'Compact controls'
},
{
text: 'Full screen'
},
]
},
{
text: 'Format',
items: [
{
text: 'Bold',
extraText: 'Ctrl+B'
},
{
text: 'Italic',
extraText: 'Ctrl+I'
},
{
text: 'Underline',
extraText: 'Ctrl+U'
},
{
text: 'Strikethrough',
extraText: 'Alt+Shift+5'
},
{
text: 'Superscript',
extraText: 'Ctrl+.'
},
{
text: 'Subscript',
extraText: 'Ctrl+,'
},
{
divider: true
},
{
text: 'Clear Formatting'
},
]
}
];
}
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
MdToolbarModule,
MdMenuModule,
MdButtonModule,
MdIconModule,
MdCardModule
],
declarations: [ App, TopMenuComponent],
bootstrap: [ App ]
})
export class AppModule { }
import { Component, Input, QueryList, ViewChildren } from '@angular/core';
import { ConnectedPositionStrategy, MdMenuTrigger } from '@angular/material';
let originWithFallbackPosition = ConnectedPositionStrategy.prototype.withFallbackPosition;
ConnectedPositionStrategy.prototype.withFallbackPosition = function (originPos, overlayPos) {
overlayPos.overlayX = 'start';
if(this._preferredPositions.length) {
return this;
}
return originWithFallbackPosition.apply(this, arguments);
};
@Component({
selector: 'top-menu',
templateUrl: 'src/top-menu.component.html'
})
export class TopMenuComponent {
@Input() items: any[];
@ViewChildren(MdMenuTrigger) triggers: QueryList<MdMenuTrigger>;
openMenu(trigger: MdMenuTrigger, level: number) {
this.triggers
.filter((x: any) => x._element.nativeElement.dataset.level >= level && x !== trigger)
.forEach(x => x.closeMenu());
trigger.openMenu();
}
closeMenu() {
this.triggers.forEach(x => x.closeMenu());
}
}
<ng-container *ngFor="let item of items">
<button md-button [mdMenuTriggerFor]="menu" #trigger="mdMenuTrigger" [attr.data-level]="1" (mouseenter)="openMenu(trigger, 1)">
{{item.text}}
</button>
<md-menu class="custom-menu" #menu="mdMenu" [overlapTrigger]="false" (close)="closeMenu()" xPosition="after">
<ng-container *ngTemplateOutlet="subMenu; context: { $implicit: item.items, level: 2 }"></ng-container>
</md-menu>
</ng-container>
<ng-template #subMenu let-items let-level="level">
<ng-container *ngFor="let item of items">
<ng-container *ngIf="item.items && item.items.length else simpleTmpl">
<button *ngIf="!item.divider" md-menu-item [disabled]="item.disabled"
[mdMenuTriggerFor]="menu"
#trigger="mdMenuTrigger"
[attr.data-level]="level"
(mouseenter)="openMenu(trigger, level);" (click)="$event.stopPropagation()">
<span>{{item.text}}</span>
<span *ngIf="item.extraText">{{item.extraText}}</span>
<md-icon *ngIf="item.icon">{{item.icon}}</md-icon>
</button>
<md-menu class="sub-menu" #menu="mdMenu" [overlapTrigger]="false" xPosition="before" >
<ng-container *ngTemplateOutlet="subMenu; context: { $implicit: item.items || [], level: level + 1 }"></ng-container>
</md-menu>
<md-divider *ngIf="item.divider" class="mat-divider"></md-divider>
</ng-container>
<ng-template #simpleTmpl>
<button *ngIf="!item.divider" md-menu-item [disabled]="item.disabled" (click)="closeMenu()">
<span>{{item.text}}</span>
<span *ngIf="item.extraText">{{item.extraText}}</span>
</button>
<md-divider *ngIf="item.divider" class="mat-divider"></md-divider>
</ng-template>
</ng-container>
</ng-template>
<md-toolbar color="primary" class="toolbar">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="80" height="80" viewBox="0, 0, 500, 500">
<g>
<path d="M250.696,82 L91,138.2 L116.235,347.426 L250.864,421.421 L386.186,346.424 L411.413,137.205 L250.696,82" fill="#FFFFFF"/>
<path d="M395.484,149.298 L250.323,99.795 L250.323,403.742 L371.974,336.416 L395.484,149.298" fill="#CA403A"/>
<path d="M108.92,150.189 L130.544,337.309 L250.32,403.742 L250.32,99.789 L108.92,150.189" fill="#E15855"/>
<path d="M286.417,252.193 L250.587,181.26 L221.196,250.973 L250.32,250.973 L286.417,252.193 M290.28,261.601 L250.323,280.287 L208.207,280.287 L188.41,329.804 L151.585,330.486 L250.323,110.832 L290.28,261.601 z" fill="#FFFFFF"/>
<path d="M250.32,110.832 L250.585,181.26 L283.934,251.027 L250.395,251.027 L250.32,280.243 L296.695,280.287 L318.372,330.492 L353.603,331.146 L250.32,110.832" fill="#FFFFFF"/>
</g>
</svg>
<h3 class="title">Ng2 Menu</h3>
<top-menu [items]="menuItems"></top-menu>
<span class="fill-remaining-space"></span>
</md-toolbar>
<md-card class="example-card">
<md-card-content>
<h2>What is an example of a material design angular2/4 menu with nested dropdown options like angular1?</h2>
<p>
I really want to incorporate angular2 material, noticed the following menu example:
</p>
<p>
https://material.angularjs.org/1.1.4/demo/menuBar
</p>
<p>This is a beauty: http://codepen.io/anon/pen/zrdQwP</p>
<p>I like how you can have nested menus, but for the Angular 2/4 demo, all you see is this:</p>
<p>https://material.angular.io/components/component/menu</p>
<p>
There is no example with nested menus! Is this possible with Angular2 + Material?
If so, can someone demonstrate how to use this? Or is this just not possible?
Seems fishy if Angular 2/4 can't support this while Angular 1.x can...
</p>
</md-card-content>
</md-card>