parent
19368085aa
commit
46efd4b938
|
@ -40,7 +40,7 @@ filegroup(
|
|||
"reflect-metadata",
|
||||
"source-map-support",
|
||||
"minimist",
|
||||
"@webcomponents/webcomponentsjs",
|
||||
"@webcomponents/custom-elements",
|
||||
"tslib",
|
||||
] for ext in [
|
||||
"*.js",
|
||||
|
|
|
@ -15,7 +15,6 @@ detection APIs.
|
|||
```ts
|
||||
//hello-world.ts
|
||||
import { Component, Input, NgModule } from '@angular/core';
|
||||
import { createNgElementConstructor, getConfigFromComponentFactory } from '@angular/elements';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world',
|
||||
|
@ -37,7 +36,7 @@ export class HelloWorldModule {}
|
|||
import { Component, NgModuleRef } from '@angular/core';
|
||||
import { createNgElementConstructor } from '@angular/elements';
|
||||
|
||||
import { HelloWorld } from './hello-world.ngfactory';
|
||||
import { HelloWorld } from './hello-world';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
|
@ -45,9 +44,8 @@ import { HelloWorld } from './hello-world.ngfactory';
|
|||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(ngModuleRef: NgModuleRef) {
|
||||
const ngElementConfig = getConfigFromComponentFactory(HelloWorld, injector);
|
||||
const NgElementConstructor = createNgElementConstructor(ngElementConfig);
|
||||
constructor(injector: Injector) {
|
||||
const NgElementConstructor = createNgElementConstructor(HelloWorld, {injector});
|
||||
customElements.register('hello-world', NgElementConstructor);
|
||||
}
|
||||
}
|
|
@ -458,9 +458,9 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/elements",
|
||||
"title": "Elements",
|
||||
"tooltip": "Exporting Angular Components as Web Components"
|
||||
"url": "guide/custom-elements",
|
||||
"title": "Custom Elements",
|
||||
"tooltip": "Using Angular Components as Custom Elements."
|
||||
},
|
||||
{
|
||||
"title": "Service Workers",
|
||||
|
|
|
@ -51,7 +51,7 @@ export const ELEMENT_MODULE_PATHS_AS_ROUTES = [
|
|||
* a custom element.
|
||||
*/
|
||||
export interface WithCustomElementComponent {
|
||||
customElementComponent: Type<string>;
|
||||
customElementComponent: Type<any>;
|
||||
}
|
||||
|
||||
/** Injection token to provide the element path modules. */
|
||||
|
|
|
@ -23,8 +23,8 @@ class FakeComponentFactory extends ComponentFactory<any> {
|
|||
create(injector: Injector,
|
||||
projectableNodes?: any[][],
|
||||
rootSelectorOrNode?: string | any,
|
||||
ngModule?: NgModuleRef<any>): ComponentRef<string> {
|
||||
return jasmine.createSpyObj('ComponentRef', ['methods']);
|
||||
ngModule?: NgModuleRef<any>): ComponentRef<any> {
|
||||
return (jasmine.createSpy('ComponentRef') as any) as ComponentRef<any>;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ const FAKE_COMPONENT_FACTORIES = new Map([
|
|||
['element-a-module-path', new FakeComponentFactory('element-a-input')]
|
||||
]);
|
||||
|
||||
describe('ElementsLoader', () => {
|
||||
fdescribe('ElementsLoader', () => {
|
||||
let elementsLoader: ElementsLoader;
|
||||
let injectedModuleRef: NgModuleRef<any>;
|
||||
let fakeCustomElements;
|
||||
|
@ -87,7 +87,7 @@ describe('ElementsLoader', () => {
|
|||
elementsLoader.loadContainingCustomElements(hostEl);
|
||||
tick(); // Tick for the module factory loader's async `load` function
|
||||
|
||||
// Call again to to check how many times registerAsCustomElements was called.
|
||||
// Call again to to check how many times customElements.define was called.
|
||||
elementsLoader.loadContainingCustomElements(hostEl);
|
||||
tick(); // Tick for the module factory loader's async `load` function
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@ export class ElementsLoader {
|
|||
|
||||
if (!selectors.length) { return of(null); }
|
||||
|
||||
selectors.forEach(s => this.register(s));
|
||||
|
||||
// Returns observable that completes when all discovered elements have been registered.
|
||||
return fromPromise(Promise.all(selectors.map(s => this.register(s))).then(result => null));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {Component, Input} from '@angular/core';
|
|||
|
||||
/** Custom element wrapper for the material expansion panel with a title input. */
|
||||
@Component({
|
||||
selector: 'expandable-section',
|
||||
selector: 'aio-expandable-section',
|
||||
templateUrl: 'expandable-section.component.html',
|
||||
})
|
||||
export class ExpandableSectionComponent {
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
"@types/source-map": "^0.5.1",
|
||||
"@types/systemjs": "0.19.32",
|
||||
"@webcomponents/custom-elements": "^1.0.4",
|
||||
"@webcomponents/webcomponentsjs": "^1.1.0",
|
||||
"angular": "npm:angular@1.6",
|
||||
"angular-1.5": "npm:angular@1.5",
|
||||
"angular-mocks": "npm:angular-mocks@1.6",
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER",
|
||||
"@angular/platform-browser": "0.0.0-PLACEHOLDER"
|
||||
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
|
||||
"rxjs": "^5.5.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* Entry point for all public APIs of the `elements` package.
|
||||
*/
|
||||
export {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from './src/element-strategy';
|
||||
export {NgElement, NgElementConfig, NgElementConstructor, createNgElementConstructor} from './src/ng-element-constructor';
|
||||
export {NgElement, NgElementConfig, NgElementConstructor, WithProperties, createNgElementConstructor} from './src/ng-element-constructor';
|
||||
export {VERSION} from './src/version';
|
||||
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
|
|
|
@ -104,7 +104,7 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
* Returns the component property value. If the component has not yet been created, the value is
|
||||
* retrieved from the cached initialization values.
|
||||
*/
|
||||
getPropertyValue(property: string): any {
|
||||
getInputValue(property: string): any {
|
||||
if (!this.componentRef) {
|
||||
return this.initialInputValues.get(property);
|
||||
}
|
||||
|
@ -116,8 +116,8 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
* Sets the input value for the property. If the component has not yet been created, the value is
|
||||
* cached and set when the component is created.
|
||||
*/
|
||||
setPropertyValue(property: string, value: any): void {
|
||||
if (strictEquals(value, this.getPropertyValue(property))) {
|
||||
setInputValue(property: string, value: any): void {
|
||||
if (strictEquals(value, this.getInputValue(property))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
this.componentFactory.inputs.forEach(({propName}) => {
|
||||
const initialValue = this.initialInputValues.get(propName);
|
||||
if (initialValue) {
|
||||
this.setPropertyValue(propName, initialValue);
|
||||
this.setInputValue(propName, initialValue);
|
||||
} else {
|
||||
// Keep track of inputs that were not initialized in case we need to know this for
|
||||
// calling ngOnChanges with SimpleChanges
|
||||
|
@ -185,8 +185,11 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
return;
|
||||
}
|
||||
|
||||
(this.componentRef !.instance as any as OnChanges).ngOnChanges(this.inputChanges);
|
||||
// Cache the changes and set inputChanges to null to capture any changes that might occur
|
||||
// during ngOnChanges.
|
||||
const inputChanges = this.inputChanges;
|
||||
this.inputChanges = null;
|
||||
(this.componentRef !.instance as any as OnChanges).ngOnChanges(inputChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,8 +202,8 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
}
|
||||
|
||||
this.scheduledChangeDetectionFn = scheduler.scheduleBeforeRender(() => {
|
||||
this.detectChanges();
|
||||
this.scheduledChangeDetectionFn = null;
|
||||
this.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -209,7 +212,7 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
*/
|
||||
protected recordInputChange(property: string, currentValue: any): void {
|
||||
// Do not record the change if the component does not implement `OnChanges`.
|
||||
if (!this.componentRef || !this.implementsOnChanges) {
|
||||
if (this.componentRef && !this.implementsOnChanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -228,7 +231,7 @@ export class ComponentFactoryNgElementStrategy implements NgElementStrategy {
|
|||
const isFirstChange = this.uninitializedInputs.has(property);
|
||||
this.uninitializedInputs.delete(property);
|
||||
|
||||
const previousValue = isFirstChange ? undefined : this.getPropertyValue(property);
|
||||
const previousValue = isFirstChange ? undefined : this.getInputValue(property);
|
||||
this.inputChanges[property] = new SimpleChange(previousValue, currentValue, isFirstChange);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ export interface NgElementStrategy {
|
|||
|
||||
connect(element: HTMLElement): void;
|
||||
disconnect(): void;
|
||||
getPropertyValue(propName: string): any;
|
||||
setPropertyValue(propName: string, value: string): void;
|
||||
getInputValue(propName: string): any;
|
||||
setInputValue(propName: string, value: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,6 +42,8 @@ export abstract class NgElement extends HTMLElement {
|
|||
/**
|
||||
* Additional type information that can be added to the NgElement class for properties added based
|
||||
* on the inputs and methods of the underlying component.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export type WithProperties<P> = {
|
||||
[property in keyof P]: P[property]
|
||||
|
@ -59,14 +61,14 @@ export interface NgElementConfig {
|
|||
injector: Injector;
|
||||
strategyFactory?: NgElementStrategyFactory;
|
||||
propertyInputs?: string[];
|
||||
attributeToPropertyInputs?: Map<string, string>;
|
||||
attributeToPropertyInputs?: {[key: string]: string};
|
||||
}
|
||||
|
||||
/** Gets a map of default set of attributes to observe and the properties they affect. */
|
||||
function getDefaultAttributeToPropertyInputs(inputs: {propName: string, templateName: string}[]) {
|
||||
const attributeToPropertyInputs = new Map<string, string>();
|
||||
const attributeToPropertyInputs: {[key: string]: string} = {};
|
||||
inputs.forEach(({propName, templateName}) => {
|
||||
attributeToPropertyInputs.set(camelToDashCase(templateName), propName);
|
||||
attributeToPropertyInputs[camelToDashCase(templateName)] = propName;
|
||||
});
|
||||
|
||||
return attributeToPropertyInputs;
|
||||
|
@ -100,7 +102,7 @@ export function createNgElementConstructor<P>(
|
|||
config.attributeToPropertyInputs || getDefaultAttributeToPropertyInputs(inputs);
|
||||
|
||||
class NgElementImpl extends NgElement {
|
||||
static readonly observedAttributes = Array.from(attributeToPropertyInputs.keys());
|
||||
static readonly observedAttributes = Object.keys(attributeToPropertyInputs);
|
||||
|
||||
constructor(strategyFactoryOverride?: NgElementStrategyFactory) {
|
||||
super();
|
||||
|
@ -113,16 +115,16 @@ export function createNgElementConstructor<P>(
|
|||
|
||||
attributeChangedCallback(
|
||||
attrName: string, oldValue: string|null, newValue: string, namespace?: string): void {
|
||||
const propName = attributeToPropertyInputs.get(attrName) !;
|
||||
this.ngElementStrategy.setPropertyValue(propName, newValue);
|
||||
const propName = attributeToPropertyInputs[attrName] !;
|
||||
this.ngElementStrategy.setInputValue(propName, newValue);
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
// Take element attribute inputs and set them as inputs on the strategy
|
||||
attributeToPropertyInputs.forEach((propName, attrName) => {
|
||||
const value = this.getAttribute(attrName);
|
||||
if (value) {
|
||||
this.ngElementStrategy.setPropertyValue(propName, value);
|
||||
NgElementImpl.observedAttributes.forEach(attrName => {
|
||||
const propName = attributeToPropertyInputs[attrName] !;
|
||||
if (this.hasAttribute(attrName)) {
|
||||
this.ngElementStrategy.setInputValue(propName, this.getAttribute(attrName) !);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -150,8 +152,8 @@ export function createNgElementConstructor<P>(
|
|||
const propertyInputs = config.propertyInputs || inputs.map(({propName}) => propName);
|
||||
propertyInputs.forEach(property => {
|
||||
Object.defineProperty(NgElementImpl.prototype, property, {
|
||||
get: function() { return this.ngElementStrategy.getPropertyValue(property); },
|
||||
set: function(newValue: any) { this.ngElementStrategy.setPropertyValue(property, newValue); },
|
||||
get: function() { return this.ngElementStrategy.getInputValue(property); },
|
||||
set: function(newValue: any) { this.ngElementStrategy.setInputValue(property, newValue); },
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
|
||||
import {Version} from '@angular/core';
|
||||
/**
|
||||
* @experimental
|
||||
|
|
|
@ -12,7 +12,6 @@ ts_library(
|
|||
"//packages/core",
|
||||
"//packages/core/testing",
|
||||
"//packages/elements",
|
||||
"//packages/elements/testing",
|
||||
"//packages/platform-browser",
|
||||
"//packages/platform-browser-dynamic",
|
||||
"//packages/platform-browser-dynamic/testing",
|
||||
|
@ -25,7 +24,7 @@ filegroup(
|
|||
name = "elements_test_bootstrap_scripts",
|
||||
# do not sort
|
||||
srcs = [
|
||||
"//:node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",
|
||||
"//:node_modules/@webcomponents/custom-elements/src/native-shim.js",
|
||||
"//:node_modules/reflect-metadata/Reflect.js",
|
||||
"//:node_modules/zone.js/dist/zone.js",
|
||||
"//:node_modules/zone.js/dist/async-test.js",
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
describe('after connected', () => {
|
||||
beforeEach(() => {
|
||||
// Set up an initial value to make sure it is passed to the component
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
strategy.connect(document.createElement('div'));
|
||||
});
|
||||
|
||||
|
@ -65,7 +65,7 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
});
|
||||
|
||||
it('should initialize the component with initial values', () => {
|
||||
expect(strategy.getPropertyValue('fooFoo')).toBe('fooFoo-1');
|
||||
expect(strategy.getInputValue('fooFoo')).toBe('fooFoo-1');
|
||||
expect(componentRef.instance.fooFoo).toBe('fooFoo-1');
|
||||
});
|
||||
|
||||
|
@ -85,15 +85,15 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
|
||||
describe('when inputs change and not connected', () => {
|
||||
it('should cache the value', () => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
expect(strategy.getPropertyValue('fooFoo')).toBe('fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
expect(strategy.getInputValue('fooFoo')).toBe('fooFoo-1');
|
||||
|
||||
// Sanity check: componentRef isn't changed since its not even on the strategy
|
||||
expect(componentRef.instance.fooFoo).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not detect changes', fakeAsync(() => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(0);
|
||||
}));
|
||||
|
@ -103,16 +103,16 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
beforeEach(() => { strategy.connect(document.createElement('div')); });
|
||||
|
||||
it('should be set on the component instance', () => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
expect(componentRef.instance.fooFoo).toBe('fooFoo-1');
|
||||
expect(strategy.getPropertyValue('fooFoo')).toBe('fooFoo-1');
|
||||
expect(strategy.getInputValue('fooFoo')).toBe('fooFoo-1');
|
||||
});
|
||||
|
||||
it('should detect changes', fakeAsync(() => {
|
||||
// Connect detected changes automatically
|
||||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(1);
|
||||
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(2);
|
||||
}));
|
||||
|
@ -121,14 +121,14 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
// Connect detected changes automatically
|
||||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(1);
|
||||
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setPropertyValue('barBar', 'barBar-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('barBar', 'barBar-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(2);
|
||||
}));
|
||||
|
||||
it('should call ngOnChanges', fakeAsync(() => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expectSimpleChanges(
|
||||
componentRef.instance.simpleChanges[0],
|
||||
|
@ -136,8 +136,8 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
}));
|
||||
|
||||
it('should call ngOnChanges once for multiple input changes', fakeAsync(() => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setPropertyValue('barBar', 'barBar-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('barBar', 'barBar-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expectSimpleChanges(componentRef.instance.simpleChanges[0], {
|
||||
fooFoo: new SimpleChange(undefined, 'fooFoo-1', true),
|
||||
|
@ -147,16 +147,16 @@ describe('ComponentFactoryNgElementStrategy', () => {
|
|||
|
||||
it('should call ngOnChanges twice for changes in different rounds with previous values',
|
||||
fakeAsync(() => {
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setPropertyValue('barBar', 'barBar-1');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-1');
|
||||
strategy.setInputValue('barBar', 'barBar-1');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expectSimpleChanges(componentRef.instance.simpleChanges[0], {
|
||||
fooFoo: new SimpleChange(undefined, 'fooFoo-1', true),
|
||||
barBar: new SimpleChange(undefined, 'barBar-1', true)
|
||||
});
|
||||
|
||||
strategy.setPropertyValue('fooFoo', 'fooFoo-2');
|
||||
strategy.setPropertyValue('barBar', 'barBar-2');
|
||||
strategy.setInputValue('fooFoo', 'fooFoo-2');
|
||||
strategy.setInputValue('barBar', 'barBar-2');
|
||||
tick(16); // scheduler waits 16ms if RAF is unavailable
|
||||
expectSimpleChanges(componentRef.instance.simpleChanges[1], {
|
||||
fooFoo: new SimpleChange('fooFoo-1', 'fooFoo-2', false),
|
||||
|
|
|
@ -13,7 +13,6 @@ import {Subject} from 'rxjs/Subject';
|
|||
|
||||
import {NgElementStrategy, NgElementStrategyEvent, NgElementStrategyFactory} from '../src/element-strategy';
|
||||
import {NgElementConstructor, createNgElementConstructor} from '../src/ng-element-constructor';
|
||||
import {patchEnv, restoreEnv} from '../testing/index';
|
||||
|
||||
type WithFooBar = {
|
||||
fooFoo: string,
|
||||
|
@ -27,7 +26,6 @@ if (typeof customElements !== 'undefined') {
|
|||
let strategyFactory: TestStrategyFactory;
|
||||
let injector: Injector;
|
||||
|
||||
beforeAll(() => patchEnv());
|
||||
beforeAll(done => {
|
||||
destroyPlatform();
|
||||
platformBrowserDynamic()
|
||||
|
@ -47,7 +45,6 @@ if (typeof customElements !== 'undefined') {
|
|||
});
|
||||
|
||||
afterAll(() => destroyPlatform());
|
||||
afterAll(() => restoreEnv());
|
||||
|
||||
it('should use a default strategy for converting component inputs', () => {
|
||||
expect(NgElementCtor.observedAttributes).toEqual(['foo-foo', 'barbar']);
|
||||
|
@ -60,8 +57,8 @@ if (typeof customElements !== 'undefined') {
|
|||
element.connectedCallback();
|
||||
expect(strategy.connectedElement).toBe(element);
|
||||
|
||||
expect(strategy.getPropertyValue('fooFoo')).toBe('value-foo-foo');
|
||||
expect(strategy.getPropertyValue('barBar')).toBe('value-barbar');
|
||||
expect(strategy.getInputValue('fooFoo')).toBe('value-foo-foo');
|
||||
expect(strategy.getInputValue('barBar')).toBe('value-barbar');
|
||||
});
|
||||
|
||||
it('should listen to output events after connected', () => {
|
||||
|
@ -108,8 +105,7 @@ if (typeof customElements !== 'undefined') {
|
|||
injector,
|
||||
strategyFactory,
|
||||
propertyInputs: ['prop1', 'prop2'],
|
||||
attributeToPropertyInputs:
|
||||
new Map<string, string>([['attr-1', 'prop1'], ['attr-2', 'prop2']])
|
||||
attributeToPropertyInputs: {'attr-1': 'prop1', 'attr-2': 'prop2'}
|
||||
});
|
||||
|
||||
customElements.define('test-element-with-changed-attributes', NgElementCtorWithChangedAttr);
|
||||
|
@ -128,9 +124,9 @@ if (typeof customElements !== 'undefined') {
|
|||
element.setAttribute('attr-3', 'value-3'); // Made-up attribute
|
||||
element.connectedCallback();
|
||||
|
||||
expect(strategy.getPropertyValue('prop1')).toBe('value-1');
|
||||
expect(strategy.getPropertyValue('prop2')).toBe('value-2');
|
||||
expect(strategy.getPropertyValue('prop3')).not.toBe('value-3');
|
||||
expect(strategy.getInputValue('prop1')).toBe('value-1');
|
||||
expect(strategy.getInputValue('prop2')).toBe('value-2');
|
||||
expect(strategy.getInputValue('prop3')).not.toBe('value-3');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -169,9 +165,9 @@ export class TestStrategy implements NgElementStrategy {
|
|||
|
||||
disconnect(): void { this.disconnectCalled = true; }
|
||||
|
||||
getPropertyValue(propName: string): any { return this.inputs.get(propName); }
|
||||
getInputValue(propName: string): any { return this.inputs.get(propName); }
|
||||
|
||||
setPropertyValue(propName: string, value: string): void { this.inputs.set(propName, value); }
|
||||
setInputValue(propName: string, value: string): void { this.inputs.set(propName, value); }
|
||||
}
|
||||
|
||||
export class TestStrategyFactory implements NgElementStrategyFactory {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "testing",
|
||||
srcs = glob(
|
||||
[
|
||||
"*.ts",
|
||||
],
|
||||
),
|
||||
module_name = "@angular/elements/testing",
|
||||
deps = [
|
||||
"//packages/core",
|
||||
"//packages/elements",
|
||||
"//packages/platform-browser",
|
||||
"@rxjs",
|
||||
],
|
||||
)
|
|
@ -1,115 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {scheduler} from '../src/utils';
|
||||
|
||||
export interface MockScheduler {
|
||||
schedule: (typeof scheduler)['schedule'];
|
||||
scheduleBeforeRender: (typeof scheduler)['scheduleBeforeRender'];
|
||||
}
|
||||
|
||||
export class AsyncMockScheduler implements MockScheduler {
|
||||
private uid = 0;
|
||||
private pendingBeforeRenderCallbacks: ({id: number, cb: () => void})[] = [];
|
||||
private pendingDelayedCallbacks: ({id: number, cb: () => void, delay: number})[] = [];
|
||||
|
||||
flushBeforeRender(): void {
|
||||
while (this.pendingBeforeRenderCallbacks.length) {
|
||||
const cb = this.pendingBeforeRenderCallbacks.shift() !.cb;
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.pendingBeforeRenderCallbacks.length = 0;
|
||||
this.pendingDelayedCallbacks.length = 0;
|
||||
}
|
||||
|
||||
schedule(cb: () => void, delay: number): () => void {
|
||||
const id = ++this.uid;
|
||||
let idx = this.pendingDelayedCallbacks.length;
|
||||
|
||||
for (let i = this.pendingDelayedCallbacks.length - 1; i >= 0; --i) {
|
||||
if (this.pendingDelayedCallbacks[i].delay <= delay) {
|
||||
idx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.pendingDelayedCallbacks.splice(idx, 0, {id, cb, delay});
|
||||
|
||||
return () => this.remove(id, this.pendingDelayedCallbacks);
|
||||
}
|
||||
|
||||
scheduleBeforeRender(cb: () => void): () => void {
|
||||
const id = ++this.uid;
|
||||
this.pendingBeforeRenderCallbacks.push({id, cb});
|
||||
return () => this.remove(id, this.pendingBeforeRenderCallbacks);
|
||||
}
|
||||
|
||||
tick(ms: number): void {
|
||||
this.flushBeforeRender();
|
||||
|
||||
this.pendingDelayedCallbacks.forEach(item => item.delay -= ms);
|
||||
this.pendingDelayedCallbacks = this.pendingDelayedCallbacks.filter(item => {
|
||||
if (item.delay <= 0) {
|
||||
const cb = item.cb;
|
||||
cb();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private remove(id: number, items: {id: number}[]): void {
|
||||
for (let i = 0, ii = items.length; i < ii; ++i) {
|
||||
if (items[i].id === id) {
|
||||
items.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncMockScheduler implements MockScheduler {
|
||||
schedule(cb: () => void, delay: number): () => void {
|
||||
cb();
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
scheduleBeforeRender(cb: () => void): () => void {
|
||||
cb();
|
||||
return () => undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function installMockScheduler(isSync?: false): AsyncMockScheduler;
|
||||
export function installMockScheduler(isSync: true): SyncMockScheduler;
|
||||
export function installMockScheduler(isSync?: boolean): AsyncMockScheduler|SyncMockScheduler {
|
||||
const mockScheduler = isSync ? new SyncMockScheduler() : new AsyncMockScheduler();
|
||||
|
||||
Object.keys(scheduler).forEach((method: keyof typeof scheduler) => {
|
||||
spyOn(scheduler, method).and.callFake(mockScheduler[method].bind(mockScheduler));
|
||||
});
|
||||
|
||||
return mockScheduler;
|
||||
}
|
||||
|
||||
export function patchEnv() {
|
||||
// This helper function is defined in `test-main.js`. See there for more details.
|
||||
// (//window as any).$$patchInnerHtmlProp();
|
||||
}
|
||||
|
||||
export function restoreEnv() {
|
||||
// This helper function is defined in `test-main.js`. See there for more details.
|
||||
//(window as any).$$restoreInnerHtmlProp();
|
||||
}
|
||||
|
||||
export function supportsCustomElements() {
|
||||
// The browser does not natively support custom elements and is not polyfillable.
|
||||
return typeof customElements !== 'undefined';
|
||||
}
|
|
@ -76,7 +76,7 @@ Promise
|
|||
.resolve()
|
||||
|
||||
// Load browser-specific polyfills for custom elements.
|
||||
.then(function() { return loadCustomElementsPolyfills(); })
|
||||
// .then(function() { return loadCustomElementsPolyfills(); })
|
||||
|
||||
// Load necessary testing packages.
|
||||
.then(function() {
|
||||
|
|
|
@ -12,7 +12,9 @@ export declare abstract class NgElement extends HTMLElement {
|
|||
|
||||
/** @experimental */
|
||||
export interface NgElementConfig {
|
||||
attributeToPropertyInputs?: Map<string, string>;
|
||||
attributeToPropertyInputs?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
injector: Injector;
|
||||
propertyInputs?: string[];
|
||||
strategyFactory?: NgElementStrategyFactory;
|
||||
|
@ -29,8 +31,8 @@ export interface NgElementStrategy {
|
|||
events: Observable<NgElementStrategyEvent>;
|
||||
connect(element: HTMLElement): void;
|
||||
disconnect(): void;
|
||||
getPropertyValue(propName: string): any;
|
||||
setPropertyValue(propName: string, value: string): void;
|
||||
getInputValue(propName: string): any;
|
||||
setInputValue(propName: string, value: string): void;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
|
@ -46,3 +48,8 @@ export interface NgElementStrategyFactory {
|
|||
|
||||
/** @experimental */
|
||||
export declare const VERSION: Version;
|
||||
|
||||
/** @experimental */
|
||||
export declare type WithProperties<P> = {
|
||||
[property in keyof P]: P[property];
|
||||
};
|
||||
|
|
|
@ -160,10 +160,6 @@
|
|||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@webcomponents/custom-elements/-/custom-elements-1.0.8.tgz#b7b8ef7248f7681d1ad4286a0ada5fe3c2bc7228"
|
||||
|
||||
"@webcomponents/webcomponentsjs@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@webcomponents/webcomponentsjs/-/webcomponentsjs-1.1.0.tgz#1392799c266fca142622a720176f688beb74d181"
|
||||
|
||||
Base64@~0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028"
|
||||
|
|
Loading…
Reference in New Issue