feat(ivy): make Hammer support tree-shakable (#32203)

Currently, it's not possible to tree-shake away the
coordination layer between HammerJS and Angular's
EventManager. This means that you get the HammerJS
support code in your production bundle whether or
not you actually use the library.

This commit removes the Hammer providers from the
default platform_browser providers list and instead
provides them as part of a `HammerModule`. Apps on
Ivy just need to import the `HammerModule` at root
to turn on Hammer support. Otherwise all Hammer code
will tree-shake away. View Engine apps will require
no change.

BREAKING CHANGE

Previously, in Ivy applications, Hammer providers
were included by default. With this commit, apps
that want Hammer support must import `HammerModule`
in their root module.

PR Close #32203
This commit is contained in:
Kara Erickson 2019-08-19 20:00:26 -07:00 committed by Andrew Kushnir
parent 0287b234ea
commit de8ebbdfd0
6 changed files with 193 additions and 14 deletions

View File

@ -21,7 +21,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime": 1440, "runtime": 1440,
"main": 136390, "main": 134091,
"polyfills": 45340 "polyfills": 45340
} }
} }

View File

@ -17,7 +17,7 @@ import {ELEMENT_PROBE_PROVIDERS} from './dom/debug/ng_probe';
import {DomRendererFactory2} from './dom/dom_renderer'; import {DomRendererFactory2} from './dom/dom_renderer';
import {DomEventsPlugin} from './dom/events/dom_events'; import {DomEventsPlugin} from './dom/events/dom_events';
import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager'; import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
import {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HammerGestureConfig, HammerGesturesPlugin} from './dom/events/hammer_gestures'; import {HAMMER_PROVIDERS} from './dom/events/hammer_gestures';
import {KeyEventsPlugin} from './dom/events/key_events'; import {KeyEventsPlugin} from './dom/events/key_events';
import {DomSharedStylesHost, SharedStylesHost} from './dom/shared_styles_host'; import {DomSharedStylesHost, SharedStylesHost} from './dom/shared_styles_host';
import {DomSanitizer, DomSanitizerImpl} from './security/dom_sanitization_service'; import {DomSanitizer, DomSanitizerImpl} from './security/dom_sanitization_service';
@ -77,13 +77,7 @@ export const BROWSER_MODULE_PROVIDERS: StaticProvider[] = [
deps: [DOCUMENT, NgZone, PLATFORM_ID] deps: [DOCUMENT, NgZone, PLATFORM_ID]
}, },
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [DOCUMENT]}, {provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [DOCUMENT]},
{ HAMMER_PROVIDERS,
provide: EVENT_MANAGER_PLUGINS,
useClass: HammerGesturesPlugin,
multi: true,
deps: [DOCUMENT, HAMMER_GESTURE_CONFIG, Console, [new Optional(), HAMMER_LOADER]]
},
{provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: []},
{ {
provide: DomRendererFactory2, provide: DomRendererFactory2,
useClass: DomRendererFactory2, useClass: DomRendererFactory2,

View File

@ -7,9 +7,11 @@
*/ */
import {DOCUMENT} from '@angular/common'; import {DOCUMENT} from '@angular/common';
import {Inject, Injectable, InjectionToken, Optional, ɵConsole as Console} from '@angular/core'; import {Inject, Injectable, InjectionToken, NgModule, Optional, Provider, ɵConsole as Console} from '@angular/core';
import {EVENT_MANAGER_PLUGINS, EventManagerPlugin} from './event_manager';
import {EventManagerPlugin} from './event_manager';
/** /**
* Supported HammerJS recognizer event names. * Supported HammerJS recognizer event names.
@ -56,6 +58,7 @@ const EVENT_NAMES = {
* DI token for providing [HammerJS](http://hammerjs.github.io/) support to Angular. * DI token for providing [HammerJS](http://hammerjs.github.io/) support to Angular.
* @see `HammerGestureConfig` * @see `HammerGestureConfig`
* *
* @ngModule HammerModule
* @publicApi * @publicApi
*/ */
export const HAMMER_GESTURE_CONFIG = new InjectionToken<HammerGestureConfig>('HammerGestureConfig'); export const HAMMER_GESTURE_CONFIG = new InjectionToken<HammerGestureConfig>('HammerGestureConfig');
@ -149,6 +152,11 @@ export class HammerGestureConfig {
} }
} }
/**
* Event plugin that adds Hammer support to an application.
*
* @ngModule HammerModule
*/
@Injectable() @Injectable()
export class HammerGesturesPlugin extends EventManagerPlugin { export class HammerGesturesPlugin extends EventManagerPlugin {
constructor( constructor(
@ -234,3 +242,40 @@ export class HammerGesturesPlugin extends EventManagerPlugin {
isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; } isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; }
} }
/**
* In Ivy, support for Hammer gestures is optional, so applications must
* import the `HammerModule` at root to turn on support. This means that
* Hammer-specific code can be tree-shaken away if not needed.
*/
export const HAMMER_PROVIDERS__POST_R3__ = [];
/**
* In View Engine, support for Hammer gestures is built-in by default.
*/
export const HAMMER_PROVIDERS__PRE_R3__: Provider[] = [
{
provide: EVENT_MANAGER_PLUGINS,
useClass: HammerGesturesPlugin,
multi: true,
deps: [DOCUMENT, HAMMER_GESTURE_CONFIG, Console, [new Optional(), HAMMER_LOADER]]
},
{provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: []},
];
export const HAMMER_PROVIDERS = HAMMER_PROVIDERS__PRE_R3__;
/**
* Adds support for HammerJS.
*
* Import this module at the root of your application so that Angular can work with
* HammerJS to detect gesture events.
*
* Note that applications still need to include the HammerJS script itself. This module
* simply sets up the coordination layer between HammerJS and Angular's EventManager.
*
* @publicApi
*/
@NgModule({providers: HAMMER_PROVIDERS__PRE_R3__})
export class HammerModule {
}

View File

@ -13,7 +13,7 @@ export {disableDebugTools, enableDebugTools} from './browser/tools/tools';
export {BrowserTransferStateModule, StateKey, TransferState, makeStateKey} from './browser/transfer_state'; export {BrowserTransferStateModule, StateKey, TransferState, makeStateKey} from './browser/transfer_state';
export {By} from './dom/debug/by'; export {By} from './dom/debug/by';
export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager'; export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
export {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HammerGestureConfig, HammerLoader} from './dom/events/hammer_gestures'; export {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HAMMER_PROVIDERS__POST_R3__ as ɵHAMMER_PROVIDERS__POST_R3__, HammerGestureConfig, HammerLoader, HammerModule} from './dom/events/hammer_gestures';
export {DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl, SafeValue} from './security/dom_sanitization_service'; export {DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl, SafeValue} from './security/dom_sanitization_service';
export * from './private_export'; export * from './private_export';

View File

@ -24,6 +24,143 @@ interface BlocklistEntry {
* repository. It should be possible to disable these tests until the component repository * repository. It should be possible to disable these tests until the component repository
* migrated the broken tests. * migrated the broken tests.
*/ */
export const testBlocklist: {[testName: string]: BlocklistEntry} = {}; export const testBlocklist: {[testName: string]: BlocklistEntry} = {
"MatSlideToggle without forms with dragging should not emit a change event when the value did not change": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should ignore clicks on the label element while dragging": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should drag from end to start": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should drag from end to start in RTL": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should update the checked property of the input": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should not drag when disabled": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should emit a change event after drag": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should drag from start to end": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms with dragging should drag from start to end in RTL": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlideToggle without forms custom action configuration should not change value on dragging when drag action is noop": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with thumb label should update the thumb label text on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set step should truncate long decimal values when using a decimal step": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set step should not add decimals to the value if it is a whole number": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set step should set the correct step value on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set step should round the value inside the label based on the provided step": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set step should snap the thumb and fill to a step on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set value should set the correct value on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with ngModel should update the model on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with input event should emit an input event while sliding": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should add and remove the mat-slider-sliding class when sliding": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should not change value without emitting a change event": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should update the value on a slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should slide to the max value when the steps do not divide evenly into it": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should set the value as max when sliding past the track": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should update the track fill on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider standard slider should set the value as min when sliding before the track": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider as a custom form control should update the control on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set min and max should set the correct value on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with set min and max should snap the fill to the nearest value on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider disabled slider should not add the mat-slider-sliding class on slide when disabled": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider disabled slider should not emit change when disabled": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider disabled slider should not change the value on slide when disabled": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with change handler should emit change on slide": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
"MatSlider slider with change handler should not emit multiple changes for same value": {
"error": "Unknown",
"notes": "Restore when Material has included HammerModule or removed dep on Hammer"
},
};
// clang-format on // clang-format on

View File

@ -60,6 +60,9 @@ export declare class HammerGestureConfig {
export declare type HammerLoader = () => Promise<void>; export declare type HammerLoader = () => Promise<void>;
export declare class HammerModule {
}
export declare function makeStateKey<T = void>(key: string): StateKey<T>; export declare function makeStateKey<T = void>(key: string): StateKey<T>;
export declare class Meta { export declare class Meta {