refactor(shadow_dom): remove `ShadowDomStrategy` in favor of `@View(encapsulation)`
BREAKING CHANGES: - `ShadowDomStrategy` was removed. To specify the encapsulation of a component use `@View(encapsulation: ViewEncapsulation.NONE | ViewEncapsulation.EMULATED | ViewEncapsulation.NATIVE)` - The default encapsulation strategy is now `ViewEncapsulation.EMULATED` if a component contains styles and `ViewEncapsulation.NONE` if it does not. Before this was always `NONE`. - `ViewLoader` now returns the template as a string and the styles as a separate array
This commit is contained in:
parent
e40ff36832
commit
16e3d7e96e
|
@ -15,7 +15,7 @@ export {
|
|||
LifecycleEvent
|
||||
} from './src/core/annotations/annotations';
|
||||
|
||||
export {ViewAnnotation} from 'angular2/src/core/annotations/view';
|
||||
export {ViewAnnotation, ViewEncapsulation} from 'angular2/src/core/annotations/view';
|
||||
export {QueryAnnotation, AttributeAnnotation} from 'angular2/src/core/annotations/di';
|
||||
|
||||
export {
|
||||
|
|
|
@ -14,5 +14,6 @@ export {
|
|||
RenderViewWithFragments,
|
||||
DomRenderer,
|
||||
DOCUMENT_TOKEN,
|
||||
APP_ID_TOKEN,
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||
} from './src/render/render';
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
Class
|
||||
} from '../../util/decorators';
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {ViewEncapsulation} from 'angular2/src/render/api';
|
||||
|
||||
/**
|
||||
* Interface for the {@link Directive} decorator function.
|
||||
|
@ -226,7 +227,7 @@ export interface ViewFactory {
|
|||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: List<Type | any | List<any>>,
|
||||
renderer?: string,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
}): ViewDecorator;
|
||||
|
@ -234,7 +235,7 @@ export interface ViewFactory {
|
|||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: List<Type | any | List<any>>,
|
||||
renderer?: string,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
}): ViewAnnotation;
|
||||
|
|
|
@ -1 +1 @@
|
|||
export {View as ViewAnnotation} from '../annotations_impl/view';
|
||||
export {View as ViewAnnotation, ViewEncapsulation} from '../annotations_impl/view';
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
||||
import {ViewEncapsulation} from 'angular2/src/render/api';
|
||||
|
||||
export {ViewEncapsulation} from 'angular2/src/render/api';
|
||||
|
||||
/**
|
||||
* Declares the available HTML templates for an application.
|
||||
|
@ -85,17 +88,17 @@ export class View {
|
|||
directives: List<Type | any | List<any>>;
|
||||
|
||||
/**
|
||||
* Specify a custom renderer for this View.
|
||||
* If this is set, neither `template`, `templateUrl`, `styles`, `styleUrls` nor `directives` are
|
||||
* used.
|
||||
* Specify how the template and the styles should be encapsulated.
|
||||
* The default is {@link ViewEncapsulation.EMULATED} if the view has styles,
|
||||
* otherwise {@link ViewEncapsulation.NONE}.
|
||||
*/
|
||||
renderer: string;
|
||||
encapsulation: ViewEncapsulation;
|
||||
|
||||
constructor({templateUrl, template, directives, renderer, styles, styleUrls}: {
|
||||
constructor({templateUrl, template, directives, encapsulation, styles, styleUrls}: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: List<Type | any | List<any>>,
|
||||
renderer?: string,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: List<string>,
|
||||
styleUrls?: List<string>,
|
||||
} = {}) {
|
||||
|
@ -104,6 +107,6 @@ export class View {
|
|||
this.styleUrls = styleUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.renderer = renderer;
|
||||
this.encapsulation = encapsulation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,10 +34,6 @@ import {List, ListWrapper} from 'angular2/src/facade/collection';
|
|||
import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/async';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {
|
||||
EmulatedUnscopedShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {XHRImpl} from 'angular2/src/render/xhr_impl';
|
||||
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||
|
@ -61,9 +57,14 @@ import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
|||
import {
|
||||
DomRenderer,
|
||||
DOCUMENT_TOKEN,
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||
} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||
DefaultDomCompiler,
|
||||
APP_ID_RANDOM_BINDING
|
||||
} from 'angular2/src/render/render';
|
||||
import {
|
||||
SharedStylesHost,
|
||||
DomSharedStylesHost
|
||||
} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
import {appComponentRefPromiseToken, appComponentTypeToken} from './application_tokens';
|
||||
|
||||
|
@ -108,12 +109,13 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||
return new EventManager(plugins, ngZone);
|
||||
},
|
||||
[NgZone]),
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
APP_ID_RANDOM_BINDING,
|
||||
DefaultDomCompiler,
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
DomSharedStylesHost,
|
||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||
ProtoViewFactory,
|
||||
AppViewPool,
|
||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||
|
|
|
@ -307,7 +307,8 @@ export class Compiler {
|
|||
templateAbsUrl: templateAbsUrl, template: view.template,
|
||||
styleAbsUrls: styleAbsUrls,
|
||||
styles: view.styles,
|
||||
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata)
|
||||
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata),
|
||||
encapsulation: view.encapsulation
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -317,10 +317,10 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||
throw 'not implemented';
|
||||
}
|
||||
bool supportsDOMEvents() {
|
||||
throw 'not implemented';
|
||||
return false;
|
||||
}
|
||||
bool supportsNativeShadowDOM() {
|
||||
throw 'not implemented';
|
||||
return false;
|
||||
}
|
||||
getHistory() {
|
||||
throw 'not implemented';
|
||||
|
|
|
@ -212,4 +212,7 @@ void iterateListLike(iter, fn(item)) {
|
|||
class SetWrapper {
|
||||
static Set createFromList(List l) => new Set.from(l);
|
||||
static bool has(Set s, key) => s.contains(key);
|
||||
static void delete(Set m, k) {
|
||||
m.remove(k);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,4 +290,5 @@ var createSetFromList: {(lst: List<any>): Set<any>} = (function() {
|
|||
export class SetWrapper {
|
||||
static createFromList<T>(lst: List<T>): Set<T> { return createSetFromList(lst); }
|
||||
static has<T>(s: Set<T>, key: T): boolean { return s.has(key); }
|
||||
static delete<K>(m: Set<K>, k: K) { m.delete(k); }
|
||||
}
|
||||
|
|
|
@ -273,6 +273,25 @@ export class RenderFragmentRef {}
|
|||
// An opaque reference to a view
|
||||
export class RenderViewRef {}
|
||||
|
||||
/**
|
||||
* How the template and styles of a view should be encapsulated.
|
||||
*/
|
||||
export enum ViewEncapsulation {
|
||||
/**
|
||||
* Emulate scoping of styles by preprocessing the style rules
|
||||
* and adding additional attributes to elements. This is the default.
|
||||
*/
|
||||
EMULATED,
|
||||
/**
|
||||
* Uses the native mechanism of the renderer. For the DOM this means creating a ShadowRoot.
|
||||
*/
|
||||
NATIVE,
|
||||
/**
|
||||
* Don't scope the template nor the styles.
|
||||
*/
|
||||
NONE
|
||||
}
|
||||
|
||||
export class ViewDefinition {
|
||||
componentId: string;
|
||||
templateAbsUrl: string;
|
||||
|
@ -280,21 +299,25 @@ export class ViewDefinition {
|
|||
directives: List<DirectiveMetadata>;
|
||||
styleAbsUrls: List<string>;
|
||||
styles: List<string>;
|
||||
encapsulation: ViewEncapsulation;
|
||||
|
||||
constructor({componentId, templateAbsUrl, template, styleAbsUrls, styles, directives}: {
|
||||
constructor({componentId, templateAbsUrl, template, styleAbsUrls, styles, directives,
|
||||
encapsulation}: {
|
||||
componentId?: string,
|
||||
templateAbsUrl?: string,
|
||||
template?: string,
|
||||
styleAbsUrls?: List<string>,
|
||||
styles?: List<string>,
|
||||
directives?: List<DirectiveMetadata>
|
||||
}) {
|
||||
directives?: List<DirectiveMetadata>,
|
||||
encapsulation?: ViewEncapsulation
|
||||
} = {}) {
|
||||
this.componentId = componentId;
|
||||
this.templateAbsUrl = templateAbsUrl;
|
||||
this.template = template;
|
||||
this.styleAbsUrls = styleAbsUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.EMULATED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export class CompileControl {
|
|||
var step = this._steps[i];
|
||||
this._parent = parent;
|
||||
this._currentStepIndex = i;
|
||||
step.process(parent, current, this);
|
||||
step.processElement(parent, current, this);
|
||||
parent = this._parent;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import {CompileElement} from './compile_element';
|
|||
import {CompileControl} from './compile_control';
|
||||
import {CompileStep} from './compile_step';
|
||||
import {ProtoViewBuilder} from '../view/proto_view_builder';
|
||||
import {ProtoViewDto, ViewType} from '../../api';
|
||||
import {ProtoViewDto, ViewType, ViewDefinition} from '../../api';
|
||||
|
||||
/**
|
||||
* CompilePipeline for executing CompileSteps recursively for
|
||||
|
@ -13,26 +13,29 @@ import {ProtoViewDto, ViewType} from '../../api';
|
|||
*/
|
||||
export class CompilePipeline {
|
||||
_control: CompileControl;
|
||||
constructor(steps: List<CompileStep>, private _useNativeShadowDom: boolean = false) {
|
||||
this._control = new CompileControl(steps);
|
||||
constructor(public steps: List<CompileStep>) { this._control = new CompileControl(steps); }
|
||||
|
||||
processStyles(styles: string[]): string[] {
|
||||
return styles.map(style => {
|
||||
this.steps.forEach(step => { style = step.processStyle(style); });
|
||||
return style;
|
||||
});
|
||||
}
|
||||
|
||||
process(rootElement: HTMLElement, protoViewType: ViewType = null,
|
||||
compilationCtxtDescription: string = ''): List<CompileElement> {
|
||||
if (isBlank(protoViewType)) {
|
||||
protoViewType = ViewType.COMPONENT;
|
||||
}
|
||||
var results = [];
|
||||
processElements(rootElement: Element, protoViewType: ViewType,
|
||||
viewDef: ViewDefinition): CompileElement[] {
|
||||
var results: CompileElement[] = [];
|
||||
var compilationCtxtDescription = viewDef.componentId;
|
||||
var rootCompileElement = new CompileElement(rootElement, compilationCtxtDescription);
|
||||
rootCompileElement.inheritedProtoView =
|
||||
new ProtoViewBuilder(rootElement, protoViewType, this._useNativeShadowDom);
|
||||
new ProtoViewBuilder(rootElement, protoViewType, viewDef.encapsulation);
|
||||
rootCompileElement.isViewRoot = true;
|
||||
this._process(results, null, rootCompileElement, compilationCtxtDescription);
|
||||
this._processElement(results, null, rootCompileElement, compilationCtxtDescription);
|
||||
return results;
|
||||
}
|
||||
|
||||
_process(results, parent: CompileElement, current: CompileElement,
|
||||
compilationCtxtDescription: string = '') {
|
||||
_processElement(results: CompileElement[], parent: CompileElement, current: CompileElement,
|
||||
compilationCtxtDescription: string = '') {
|
||||
var additionalChildren = this._control.internalProcess(results, 0, parent, current);
|
||||
|
||||
if (current.compileChildren) {
|
||||
|
@ -46,7 +49,7 @@ export class CompilePipeline {
|
|||
childCompileElement.inheritedProtoView = current.inheritedProtoView;
|
||||
childCompileElement.inheritedElementBinder = current.inheritedElementBinder;
|
||||
childCompileElement.distanceToInheritedBinder = current.distanceToInheritedBinder + 1;
|
||||
this._process(results, current, childCompileElement);
|
||||
this._processElement(results, current, childCompileElement);
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
|
@ -54,7 +57,7 @@ export class CompilePipeline {
|
|||
|
||||
if (isPresent(additionalChildren)) {
|
||||
for (var i = 0; i < additionalChildren.length; i++) {
|
||||
this._process(results, current, additionalChildren[i]);
|
||||
this._processElement(results, current, additionalChildren[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import * as compileControlModule from './compile_control';
|
|||
* Is guaranteed to be called in depth first order
|
||||
*/
|
||||
export interface CompileStep {
|
||||
process(parent: CompileElement, current: CompileElement,
|
||||
control: compileControlModule.CompileControl): void;
|
||||
processElement(parent: CompileElement, current: CompileElement,
|
||||
control: compileControlModule.CompileControl): void;
|
||||
|
||||
processStyle(style: string): string;
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@ import {PropertyBindingParser} from './property_binding_parser';
|
|||
import {TextInterpolationParser} from './text_interpolation_parser';
|
||||
import {DirectiveParser} from './directive_parser';
|
||||
import {ViewSplitter} from './view_splitter';
|
||||
import {ShadowDomCompileStep} from '../shadow_dom/shadow_dom_compile_step';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
import {StyleEncapsulator} from './style_encapsulator';
|
||||
|
||||
export class CompileStepFactory {
|
||||
createSteps(view: ViewDefinition): List<CompileStep> { return null; }
|
||||
}
|
||||
|
||||
export class DefaultStepFactory extends CompileStepFactory {
|
||||
constructor(public _parser: Parser, public _shadowDomStrategy: ShadowDomStrategy) { super(); }
|
||||
private _componentUIDsCache: Map<string, string> = new Map();
|
||||
constructor(private _parser: Parser, private _appId: string) { super(); }
|
||||
|
||||
createSteps(view: ViewDefinition): List<CompileStep> {
|
||||
return [
|
||||
|
@ -22,7 +22,7 @@ export class DefaultStepFactory extends CompileStepFactory {
|
|||
new PropertyBindingParser(this._parser),
|
||||
new DirectiveParser(this._parser, view.directives),
|
||||
new TextInterpolationParser(this._parser),
|
||||
new ShadowDomCompileStep(this._shadowDomStrategy, view)
|
||||
new StyleEncapsulator(this._appId, view, this._componentUIDsCache)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {BaseException, isPresent} from 'angular2/src/facade/lang';
|
||||
import {BaseException, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
|
@ -11,14 +11,18 @@ import {
|
|||
DirectiveMetadata,
|
||||
RenderCompiler,
|
||||
RenderProtoViewRef,
|
||||
RenderProtoViewMergeMapping
|
||||
RenderProtoViewMergeMapping,
|
||||
ViewEncapsulation
|
||||
} from '../../api';
|
||||
import {CompilePipeline} from './compile_pipeline';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
|
||||
import {Parser} from 'angular2/src/change_detection/change_detection';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
import * as pvm from '../view/proto_view_merger';
|
||||
import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens';
|
||||
import {Inject} from 'angular2/di';
|
||||
import {SharedStylesHost} from '../view/shared_styles_host';
|
||||
import {prependAll} from '../util';
|
||||
|
||||
/**
|
||||
* The compiler loads and translates the html templates of components into
|
||||
|
@ -26,15 +30,17 @@ import * as pvm from '../view/proto_view_merger';
|
|||
* the CompilePipeline and the CompileSteps.
|
||||
*/
|
||||
export class DomCompiler extends RenderCompiler {
|
||||
constructor(public _stepFactory: CompileStepFactory, public _viewLoader: ViewLoader,
|
||||
public _useNativeShadowDom: boolean) {
|
||||
constructor(private _stepFactory: CompileStepFactory, private _viewLoader: ViewLoader,
|
||||
private _sharedStylesHost: SharedStylesHost) {
|
||||
super();
|
||||
}
|
||||
|
||||
compile(view: ViewDefinition): Promise<ProtoViewDto> {
|
||||
var tplPromise = this._viewLoader.load(view);
|
||||
return PromiseWrapper.then(
|
||||
tplPromise, (el) => this._compileTemplate(view, el, ViewType.COMPONENT), (e) => {
|
||||
tplPromise, (tplAndStyles: TemplateAndStyles) =>
|
||||
this._compileView(view, tplAndStyles, ViewType.COMPONENT),
|
||||
(e) => {
|
||||
throw new BaseException(`Failed to load the template for "${view.componentId}" : ${e}`);
|
||||
return null;
|
||||
});
|
||||
|
@ -46,11 +52,13 @@ export class DomCompiler extends RenderCompiler {
|
|||
templateAbsUrl: null, template: null,
|
||||
styles: null,
|
||||
styleAbsUrls: null,
|
||||
directives: [directiveMetadata]
|
||||
directives: [directiveMetadata],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
});
|
||||
var template = DOM.createTemplate('');
|
||||
DOM.appendChild(DOM.content(template), DOM.createElement(directiveMetadata.selector));
|
||||
return this._compileTemplate(hostViewDef, template, ViewType.HOST);
|
||||
return this._compileView(
|
||||
hostViewDef, new TemplateAndStyles(
|
||||
`<${directiveMetadata.selector}></${directiveMetadata.selector}>`, []),
|
||||
ViewType.HOST);
|
||||
}
|
||||
|
||||
mergeProtoViewsRecursively(
|
||||
|
@ -58,20 +66,47 @@ export class DomCompiler extends RenderCompiler {
|
|||
return PromiseWrapper.resolve(pvm.mergeProtoViewsRecursively(protoViewRefs));
|
||||
}
|
||||
|
||||
_compileTemplate(viewDef: ViewDefinition, tplElement,
|
||||
protoViewType: ViewType): Promise<ProtoViewDto> {
|
||||
var pipeline =
|
||||
new CompilePipeline(this._stepFactory.createSteps(viewDef), this._useNativeShadowDom);
|
||||
var compileElements = pipeline.process(tplElement, protoViewType, viewDef.componentId);
|
||||
_compileView(viewDef: ViewDefinition, templateAndStyles: TemplateAndStyles,
|
||||
protoViewType: ViewType): Promise<ProtoViewDto> {
|
||||
if (viewDef.encapsulation === ViewEncapsulation.EMULATED &&
|
||||
templateAndStyles.styles.length === 0) {
|
||||
viewDef = this._normalizeViewEncapsulationIfThereAreNoStyles(viewDef);
|
||||
}
|
||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef));
|
||||
|
||||
var compiledStyles = pipeline.processStyles(templateAndStyles.styles);
|
||||
var compileElements = pipeline.processElements(DOM.createTemplate(templateAndStyles.template),
|
||||
protoViewType, viewDef);
|
||||
if (viewDef.encapsulation === ViewEncapsulation.NATIVE) {
|
||||
prependAll(DOM.content(compileElements[0].element),
|
||||
compiledStyles.map(style => DOM.createStyleElement(style)));
|
||||
} else {
|
||||
this._sharedStylesHost.addStyles(compiledStyles);
|
||||
}
|
||||
|
||||
return PromiseWrapper.resolve(compileElements[0].inheritedProtoView.build());
|
||||
}
|
||||
|
||||
_normalizeViewEncapsulationIfThereAreNoStyles(viewDef: ViewDefinition): ViewDefinition {
|
||||
if (viewDef.encapsulation === ViewEncapsulation.EMULATED) {
|
||||
return new ViewDefinition({
|
||||
componentId: viewDef.componentId,
|
||||
templateAbsUrl: viewDef.templateAbsUrl, template: viewDef.template,
|
||||
styleAbsUrls: viewDef.styleAbsUrls,
|
||||
styles: viewDef.styles,
|
||||
directives: viewDef.directives,
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
});
|
||||
} else {
|
||||
return viewDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DefaultDomCompiler extends DomCompiler {
|
||||
constructor(parser: Parser, shadowDomStrategy: ShadowDomStrategy, viewLoader: ViewLoader) {
|
||||
super(new DefaultStepFactory(parser, shadowDomStrategy), viewLoader,
|
||||
shadowDomStrategy.hasNativeContentElement());
|
||||
constructor(parser: Parser, viewLoader: ViewLoader, sharedStylesHost: SharedStylesHost,
|
||||
@Inject(APP_ID_TOKEN) appId: any) {
|
||||
super(new DefaultStepFactory(parser, appId), viewLoader, sharedStylesHost);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ export class DirectiveParser implements CompileStep {
|
|||
}
|
||||
}
|
||||
|
||||
processStyle(style: string): string { return style; }
|
||||
|
||||
_ensureComponentOnlyHasElementSelector(selector, directive) {
|
||||
var isElementSelector = selector.length === 1 && selector[0].isElementSelector();
|
||||
if (!isElementSelector && directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
|
@ -37,7 +39,7 @@ export class DirectiveParser implements CompileStep {
|
|||
}
|
||||
}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var classList = current.classList();
|
||||
var cssSelector = new CssSelector();
|
||||
|
|
|
@ -25,7 +25,9 @@ var BIND_NAME_REGEXP =
|
|||
export class PropertyBindingParser implements CompileStep {
|
||||
constructor(private _parser: Parser) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processStyle(style: string): string { return style; }
|
||||
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var newAttrs = new Map();
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import {CompileStep} from '../compiler/compile_step';
|
||||
import {CompileElement} from '../compiler/compile_element';
|
||||
import {CompileControl} from '../compiler/compile_control';
|
||||
import {ViewDefinition, ViewEncapsulation, ViewType} from '../../api';
|
||||
import {NG_CONTENT_ELEMENT_NAME, isElementWithTag} from '../util';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {ShadowCss} from './shadow_css';
|
||||
|
||||
export class StyleEncapsulator implements CompileStep {
|
||||
constructor(private _appId: string, private _view: ViewDefinition,
|
||||
private _componentUIDsCache: Map<string, string>) {}
|
||||
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
if (isElementWithTag(current.element, NG_CONTENT_ELEMENT_NAME)) {
|
||||
current.inheritedProtoView.bindNgContent();
|
||||
} else {
|
||||
if (this._view.encapsulation === ViewEncapsulation.EMULATED) {
|
||||
this._processEmulatedScopedElement(current, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processStyle(style: string): string {
|
||||
var encapsulation = this._view.encapsulation;
|
||||
if (encapsulation === ViewEncapsulation.EMULATED) {
|
||||
return this._shimCssForComponent(style, this._view.componentId);
|
||||
} else {
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
_processEmulatedScopedElement(current: CompileElement, parent: CompileElement): void {
|
||||
var element = current.element;
|
||||
var hostComponentId = this._view.componentId;
|
||||
var viewType = current.inheritedProtoView.type;
|
||||
// Shim the element as a child of the compiled component
|
||||
if (viewType !== ViewType.HOST && isPresent(hostComponentId)) {
|
||||
var contentAttribute = getContentAttribute(this._getComponentId(hostComponentId));
|
||||
DOM.setAttribute(element, contentAttribute, '');
|
||||
// also shim the host
|
||||
if (isBlank(parent) && viewType == ViewType.COMPONENT) {
|
||||
var hostAttribute = getHostAttribute(this._getComponentId(hostComponentId));
|
||||
current.inheritedProtoView.setHostAttribute(hostAttribute, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_shimCssForComponent(cssText: string, componentId: string): string {
|
||||
var id = this._getComponentId(componentId);
|
||||
var shadowCss = new ShadowCss();
|
||||
return shadowCss.shimCssText(cssText, getContentAttribute(id), getHostAttribute(id));
|
||||
}
|
||||
|
||||
_getComponentId(componentStringId: string): string {
|
||||
var id = this._componentUIDsCache.get(componentStringId);
|
||||
if (isBlank(id)) {
|
||||
id = `${this._appId}-${this._componentUIDsCache.size}`;
|
||||
this._componentUIDsCache.set(componentStringId, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the attribute to be added to the component
|
||||
function getHostAttribute(compId: string): string {
|
||||
return `_nghost-${compId}`;
|
||||
}
|
||||
|
||||
// Returns the attribute to be added on every single element nodes in the component
|
||||
function getContentAttribute(compId: string): string {
|
||||
return `_ngcontent-${compId}`;
|
||||
}
|
|
@ -13,7 +13,9 @@ import {CompileControl} from './compile_control';
|
|||
export class TextInterpolationParser implements CompileStep {
|
||||
constructor(public _parser: Parser) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processStyle(style: string): string { return style; }
|
||||
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
if (!current.compileChildren) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -10,14 +10,17 @@ import {
|
|||
import {Map, MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ViewDefinition} from '../../api';
|
||||
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
import {ViewDefinition} from '../../api';
|
||||
|
||||
import {StyleInliner} from './style_inliner';
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
|
||||
export class TemplateAndStyles {
|
||||
constructor(public template: string, public styles: string[]) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to load component views.
|
||||
* TODO: Make public API once we are more confident in this approach.
|
||||
|
@ -29,33 +32,32 @@ export class ViewLoader {
|
|||
constructor(private _xhr: XHR, private _styleInliner: StyleInliner,
|
||||
private _styleUrlResolver: StyleUrlResolver) {}
|
||||
|
||||
load(view: ViewDefinition): Promise</*element*/ any> {
|
||||
let tplElAndStyles: List<string | Promise<string>> = [this._loadHtml(view)];
|
||||
|
||||
if (isPresent(view.styles)) {
|
||||
view.styles.forEach((cssText: string) => {
|
||||
let textOrPromise = this._resolveAndInlineCssText(cssText, view.templateAbsUrl);
|
||||
tplElAndStyles.push(textOrPromise);
|
||||
load(viewDef: ViewDefinition): Promise<TemplateAndStyles> {
|
||||
let tplAndStyles: List<Promise<TemplateAndStyles>| Promise<string>| string> =
|
||||
[this._loadHtml(viewDef.template, viewDef.templateAbsUrl)];
|
||||
if (isPresent(viewDef.styles)) {
|
||||
viewDef.styles.forEach((cssText: string) => {
|
||||
let textOrPromise = this._resolveAndInlineCssText(cssText, viewDef.templateAbsUrl);
|
||||
tplAndStyles.push(textOrPromise);
|
||||
});
|
||||
}
|
||||
|
||||
if (isPresent(view.styleAbsUrls)) {
|
||||
view.styleAbsUrls.forEach(url => {
|
||||
if (isPresent(viewDef.styleAbsUrls)) {
|
||||
viewDef.styleAbsUrls.forEach(url => {
|
||||
let promise = this._loadText(url).then(
|
||||
cssText => this._resolveAndInlineCssText(cssText, view.templateAbsUrl));
|
||||
tplElAndStyles.push(promise);
|
||||
cssText => this._resolveAndInlineCssText(cssText, viewDef.templateAbsUrl));
|
||||
tplAndStyles.push(promise);
|
||||
});
|
||||
}
|
||||
|
||||
// Inline the styles from the @View annotation and return a template element
|
||||
return PromiseWrapper.all(tplElAndStyles)
|
||||
.then((res: List<string>) => {
|
||||
let tplEl = res[0];
|
||||
let cssTexts = ListWrapper.slice(res, 1);
|
||||
// Inline the styles from the @View annotation
|
||||
return PromiseWrapper.all(tplAndStyles)
|
||||
.then((res: List<TemplateAndStyles | string>) => {
|
||||
let loadedTplAndStyles = <TemplateAndStyles>res[0];
|
||||
let styles = <string[]>ListWrapper.slice(res, 1);
|
||||
|
||||
_insertCssTexts(DOM.content(tplEl), cssTexts);
|
||||
|
||||
return tplEl;
|
||||
return new TemplateAndStyles(loadedTplAndStyles.template,
|
||||
loadedTplAndStyles.styles.concat(styles));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -77,40 +79,54 @@ export class ViewLoader {
|
|||
}
|
||||
|
||||
// Load the html and inline any style tags
|
||||
private _loadHtml(view: ViewDefinition): Promise<any /* element */> {
|
||||
private _loadHtml(template: string, templateAbsUrl: string): Promise<TemplateAndStyles> {
|
||||
let html;
|
||||
|
||||
// Load the HTML
|
||||
if (isPresent(view.template)) {
|
||||
html = PromiseWrapper.resolve(view.template);
|
||||
} else if (isPresent(view.templateAbsUrl)) {
|
||||
html = this._loadText(view.templateAbsUrl);
|
||||
if (isPresent(template)) {
|
||||
html = PromiseWrapper.resolve(template);
|
||||
} else if (isPresent(templateAbsUrl)) {
|
||||
html = this._loadText(templateAbsUrl);
|
||||
} else {
|
||||
throw new BaseException('View should have either the templateUrl or template property set');
|
||||
}
|
||||
|
||||
return html.then(html => {
|
||||
var tplEl = DOM.createTemplate(html);
|
||||
|
||||
// Replace $baseUrl with the base url for the template
|
||||
let templateAbsUrl = view.templateAbsUrl;
|
||||
if (isPresent(templateAbsUrl) && templateAbsUrl.indexOf("/") >= 0) {
|
||||
let baseUrl = templateAbsUrl.substring(0, templateAbsUrl.lastIndexOf("/"));
|
||||
this._substituteBaseUrl(DOM.content(tplEl), baseUrl);
|
||||
}
|
||||
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
|
||||
let unresolvedStyles: string[] = [];
|
||||
for (let i = 0; i < styleEls.length; i++) {
|
||||
var styleEl = styleEls[i];
|
||||
unresolvedStyles.push(DOM.getText(styleEl));
|
||||
DOM.remove(styleEl);
|
||||
}
|
||||
|
||||
let syncStyles: string[] = [];
|
||||
let asyncStyles: Promise<string>[] = [];
|
||||
|
||||
// Inline the style tags from the html
|
||||
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
|
||||
|
||||
let promises: List<Promise<string>> = [];
|
||||
for (let i = 0; i < styleEls.length; i++) {
|
||||
let promise = this._resolveAndInlineElement(styleEls[i], view.templateAbsUrl);
|
||||
if (isPromise(promise)) {
|
||||
promises.push(promise);
|
||||
let styleEl = styleEls[i];
|
||||
let resolvedStyled = this._resolveAndInlineCssText(DOM.getText(styleEl), templateAbsUrl);
|
||||
if (isPromise(resolvedStyled)) {
|
||||
asyncStyles.push(<Promise<string>>resolvedStyled);
|
||||
} else {
|
||||
syncStyles.push(<string>resolvedStyled);
|
||||
}
|
||||
}
|
||||
|
||||
return promises.length > 0 ? PromiseWrapper.all(promises).then(_ => tplEl) : tplEl;
|
||||
if (asyncStyles.length === 0) {
|
||||
return PromiseWrapper.resolve(new TemplateAndStyles(DOM.getInnerHTML(tplEl), syncStyles));
|
||||
} else {
|
||||
return PromiseWrapper.all(asyncStyles)
|
||||
.then(loadedStyles => new TemplateAndStyles(DOM.getInnerHTML(tplEl),
|
||||
syncStyles.concat(<string[]>loadedStyles)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -139,43 +155,8 @@ export class ViewLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inlines a style element.
|
||||
*
|
||||
* @param styleEl The style element
|
||||
* @param baseUrl The base url
|
||||
* @returns {Promise<any>} null when no @import rule exist in the css or a Promise
|
||||
* @private
|
||||
*/
|
||||
private _resolveAndInlineElement(styleEl, baseUrl: string): Promise<any> {
|
||||
let textOrPromise = this._resolveAndInlineCssText(DOM.getText(styleEl), baseUrl);
|
||||
|
||||
if (isPromise(textOrPromise)) {
|
||||
return (<Promise<string>>textOrPromise).then(css => { DOM.setText(styleEl, css); });
|
||||
} else {
|
||||
DOM.setText(styleEl, <string>textOrPromise);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _resolveAndInlineCssText(cssText: string, baseUrl: string): string | Promise<string> {
|
||||
cssText = this._styleUrlResolver.resolveUrls(cssText, baseUrl);
|
||||
return this._styleInliner.inlineImports(cssText, baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
function _insertCssTexts(element, cssTexts: List<string>): void {
|
||||
if (cssTexts.length == 0) return;
|
||||
|
||||
let insertBefore = DOM.firstChild(element);
|
||||
|
||||
for (let i = cssTexts.length - 1; i >= 0; i--) {
|
||||
let styleEl = DOM.createStyleElement(cssTexts[i]);
|
||||
if (isPresent(insertBefore)) {
|
||||
DOM.insertBefore(insertBefore, styleEl);
|
||||
} else {
|
||||
DOM.appendChild(element, styleEl);
|
||||
}
|
||||
insertBefore = styleEl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ import {dashCaseToCamelCase} from '../util';
|
|||
export class ViewSplitter implements CompileStep {
|
||||
constructor(public _parser: Parser) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processStyle(style: string): string { return style; }
|
||||
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var templateBindings = attrs.get('template');
|
||||
var hasTemplateBinding = isPresent(templateBindings);
|
||||
|
|
|
@ -15,6 +15,7 @@ import {EventManager} from './events/event_manager';
|
|||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './view/proto_view';
|
||||
import {DomView, DomViewRef, resolveInternalDomView} from './view/view';
|
||||
import {DomFragmentRef, resolveInternalDomFragment} from './view/fragment';
|
||||
import {DomSharedStylesHost} from './view/shared_styles_host';
|
||||
import {
|
||||
NG_BINDING_CLASS_SELECTOR,
|
||||
NG_BINDING_CLASS,
|
||||
|
@ -31,9 +32,8 @@ import {
|
|||
RenderViewWithFragments
|
||||
} from '../api';
|
||||
|
||||
export const DOCUMENT_TOKEN: OpaqueToken = CONST_EXPR(new OpaqueToken('DocumentToken'));
|
||||
export const DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES: OpaqueToken =
|
||||
CONST_EXPR(new OpaqueToken('DomReflectPropertiesAsAttributes'));
|
||||
import {DOCUMENT_TOKEN, DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from './dom_tokens';
|
||||
|
||||
const REFLECT_PREFIX: string = 'ng-reflect-';
|
||||
|
||||
@Injectable()
|
||||
|
@ -41,7 +41,8 @@ export class DomRenderer extends Renderer {
|
|||
_document;
|
||||
_reflectPropertiesAsAttributes: boolean;
|
||||
|
||||
constructor(public _eventManager: EventManager, @Inject(DOCUMENT_TOKEN) document,
|
||||
constructor(public _eventManager: EventManager, private _domSharedStylesHost: DomSharedStylesHost,
|
||||
@Inject(DOCUMENT_TOKEN) document,
|
||||
@Inject(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES) reflectPropertiesAsAttributes:
|
||||
boolean) {
|
||||
super();
|
||||
|
@ -65,7 +66,14 @@ export class DomRenderer extends Renderer {
|
|||
}
|
||||
|
||||
destroyView(viewRef: RenderViewRef) {
|
||||
// noop for now
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
var elementBinders = view.proto.elementBinders;
|
||||
for (var i = 0; i < elementBinders.length; i++) {
|
||||
var binder = elementBinders[i];
|
||||
if (binder.hasNativeShadowRoot) {
|
||||
this._domSharedStylesHost.removeHost(DOM.getShadowRoot(view.boundElements[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getNativeElementSync(location: RenderElementRef): any {
|
||||
|
@ -226,7 +234,9 @@ export class DomRenderer extends Renderer {
|
|||
// native shadow DOM
|
||||
if (binder.hasNativeShadowRoot) {
|
||||
var shadowRootWrapper = DOM.firstChild(element);
|
||||
moveChildNodes(shadowRootWrapper, DOM.createShadowRoot(element));
|
||||
var shadowRoot = DOM.createShadowRoot(element);
|
||||
this._domSharedStylesHost.addHost(shadowRoot);
|
||||
moveChildNodes(shadowRootWrapper, shadowRoot);
|
||||
DOM.remove(shadowRootWrapper);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import {OpaqueToken, bind, Binding} from 'angular2/di';
|
||||
import {CONST_EXPR, StringWrapper, Math} from 'angular2/src/facade/lang';
|
||||
|
||||
export const DOCUMENT_TOKEN: OpaqueToken = CONST_EXPR(new OpaqueToken('DocumentToken'));
|
||||
|
||||
export const DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES: OpaqueToken =
|
||||
CONST_EXPR(new OpaqueToken('DomReflectPropertiesAsAttributes'));
|
||||
|
||||
|
||||
/**
|
||||
* A unique id (string) for an angular application.
|
||||
*/
|
||||
export const APP_ID_TOKEN: OpaqueToken = CONST_EXPR(new OpaqueToken('AppId'));
|
||||
|
||||
/**
|
||||
* Bindings that will generate a random APP_ID_TOKEN.
|
||||
*/
|
||||
export var APP_ID_RANDOM_BINDING: Binding =
|
||||
bind(APP_ID_TOKEN).toFactory(() => `${randomChar()}${randomChar()}${randomChar()}`, []);
|
||||
|
||||
|
||||
function randomChar(): string {
|
||||
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {EmulatedUnscopedShadowDomStrategy} from './emulated_unscoped_shadow_dom_strategy';
|
||||
import {
|
||||
getContentAttribute,
|
||||
getHostAttribute,
|
||||
getComponentId,
|
||||
shimCssForComponent,
|
||||
insertStyleElement
|
||||
} from './util';
|
||||
|
||||
/**
|
||||
* This strategy emulates the Shadow DOM for the templates, styles **included**:
|
||||
* - components templates are added as children of their component element,
|
||||
* - both the template and the styles are modified so that styles are scoped to the component
|
||||
* they belong to,
|
||||
* - styles are moved from the templates to the styleHost (i.e. the document head).
|
||||
*
|
||||
* Notes:
|
||||
* - styles are scoped to their component and will apply only to it,
|
||||
* - a common subset of shadow DOM selectors are supported,
|
||||
* - see `ShadowCss` for more information and limitations.
|
||||
*/
|
||||
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
|
||||
constructor(styleHost) { super(styleHost); }
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl: Element): void {
|
||||
let cssText = DOM.getText(styleEl);
|
||||
cssText = shimCssForComponent(cssText, hostComponentId);
|
||||
DOM.setText(styleEl, cssText);
|
||||
this._moveToStyleHost(styleEl);
|
||||
}
|
||||
|
||||
_moveToStyleHost(styleEl): void {
|
||||
DOM.remove(styleEl);
|
||||
insertStyleElement(this.styleHost, styleEl);
|
||||
}
|
||||
|
||||
processElement(hostComponentId: string, elementComponentId: string, element: Element): void {
|
||||
// Shim the element as a child of the compiled component
|
||||
if (isPresent(hostComponentId)) {
|
||||
var contentAttribute = getContentAttribute(getComponentId(hostComponentId));
|
||||
DOM.setAttribute(element, contentAttribute, '');
|
||||
}
|
||||
// If the current element is also a component, shim it as a host
|
||||
if (isPresent(elementComponentId)) {
|
||||
var hostAttribute = getHostAttribute(getComponentId(elementComponentId));
|
||||
DOM.setAttribute(element, hostAttribute, '');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {insertSharedStyleText} from './util';
|
||||
|
||||
/**
|
||||
* This strategy emulates the Shadow DOM for the templates, styles **excluded**:
|
||||
* - components templates are added as children of their component element,
|
||||
* - styles are moved from the templates to the styleHost (i.e. the document head).
|
||||
*
|
||||
* Notes:
|
||||
* - styles are **not** scoped to their component and will apply to the whole document,
|
||||
* - you can **not** use shadow DOM specific selectors in the styles
|
||||
*/
|
||||
export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||
constructor(public styleHost) { super(); }
|
||||
|
||||
hasNativeContentElement(): boolean { return false; }
|
||||
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleEl: Element): void {
|
||||
var cssText = DOM.getText(styleEl);
|
||||
insertSharedStyleText(cssText, this.styleHost, styleEl);
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
|
||||
/**
|
||||
* This strategies uses the native Shadow DOM support.
|
||||
*
|
||||
* The templates for the component are inserted in a Shadow Root created on the component element.
|
||||
* Hence they are strictly isolated.
|
||||
*/
|
||||
@Injectable()
|
||||
export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
hasNativeContentElement(): boolean { return true; }
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import {CompileStep} from '../compiler/compile_step';
|
||||
import {CompileElement} from '../compiler/compile_element';
|
||||
import {CompileControl} from '../compiler/compile_control';
|
||||
import {ViewDefinition} from '../../api';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {NG_CONTENT_ELEMENT_NAME, isElementWithTag} from '../util';
|
||||
|
||||
export class ShadowDomCompileStep implements CompileStep {
|
||||
constructor(public _shadowDomStrategy: ShadowDomStrategy, public _view: ViewDefinition) {}
|
||||
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
if (isElementWithTag(current.element, NG_CONTENT_ELEMENT_NAME)) {
|
||||
current.inheritedProtoView.bindNgContent();
|
||||
} else if (isElementWithTag(current.element, 'style')) {
|
||||
this._processStyleElement(current, control);
|
||||
} else {
|
||||
var componentId = current.isBound() ? current.inheritedElementBinder.componentId : null;
|
||||
this._shadowDomStrategy.processElement(this._view.componentId, componentId, current.element);
|
||||
}
|
||||
}
|
||||
|
||||
_processStyleElement(current: CompileElement, control: CompileControl) {
|
||||
this._shadowDomStrategy.processStyleElement(this._view.componentId, this._view.templateAbsUrl,
|
||||
current.element);
|
||||
|
||||
// Style elements should not be further processed by the compiler, as they can not contain
|
||||
// bindings. Skipping further compiler steps allow speeding up the compilation process.
|
||||
control.ignoreCurrentElement();
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
export class ShadowDomStrategy {
|
||||
// Whether the strategy understands the native <content> tag
|
||||
hasNativeContentElement(): boolean { return true; }
|
||||
|
||||
// An optional step that can modify the template style elements.
|
||||
processStyleElement(hostComponentId: string, templateUrl: string, styleElement: HTMLStyleElement):
|
||||
void {}
|
||||
|
||||
// An optional step that can modify the template elements (style elements exlcuded).
|
||||
processElement(hostComponentId: string, elementComponentId: string, element: HTMLElement): void {}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ShadowCss} from './shadow_css';
|
||||
|
||||
var _componentUIDs: Map<string, int> = new Map();
|
||||
var _nextComponentUID: int = 0;
|
||||
var _sharedStyleTexts: Map<string, boolean> = new Map();
|
||||
var _lastInsertedStyleEl;
|
||||
|
||||
export function getComponentId(componentStringId: string): number {
|
||||
var id = _componentUIDs.get(componentStringId);
|
||||
if (isBlank(id)) {
|
||||
id = _nextComponentUID++;
|
||||
_componentUIDs.set(componentStringId, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export function insertSharedStyleText(cssText, styleHost, styleEl) {
|
||||
if (!_sharedStyleTexts.has(cssText)) {
|
||||
// Styles are unscoped and shared across components, only append them to the head
|
||||
// when there are not present yet
|
||||
_sharedStyleTexts.set(cssText, true);
|
||||
insertStyleElement(styleHost, styleEl);
|
||||
}
|
||||
}
|
||||
|
||||
export function insertStyleElement(host, styleEl) {
|
||||
if (isBlank(_lastInsertedStyleEl)) {
|
||||
var firstChild = DOM.firstChild(host);
|
||||
if (isPresent(firstChild)) {
|
||||
DOM.insertBefore(firstChild, styleEl);
|
||||
} else {
|
||||
DOM.appendChild(host, styleEl);
|
||||
}
|
||||
} else {
|
||||
DOM.insertAfter(_lastInsertedStyleEl, styleEl);
|
||||
}
|
||||
_lastInsertedStyleEl = styleEl;
|
||||
}
|
||||
|
||||
// Return the attribute to be added to the component
|
||||
export function getHostAttribute(id: int): string {
|
||||
return `_nghost-${id}`;
|
||||
}
|
||||
|
||||
// Returns the attribute to be added on every single element nodes in the component
|
||||
export function getContentAttribute(id: int): string {
|
||||
return `_ngcontent-${id}`;
|
||||
}
|
||||
|
||||
export function shimCssForComponent(cssText: string, componentId: string): string {
|
||||
var id = getComponentId(componentId);
|
||||
var shadowCss = new ShadowCss();
|
||||
return shadowCss.shimCssText(cssText, getContentAttribute(id), getHostAttribute(id));
|
||||
}
|
||||
|
||||
// Reset the caches - used for tests only
|
||||
export function resetShadowDomCache() {
|
||||
_componentUIDs.clear();
|
||||
_nextComponentUID = 0;
|
||||
_sharedStyleTexts.clear();
|
||||
_lastInsertedStyleEl = null;
|
||||
}
|
|
@ -122,4 +122,21 @@ export function queryBoundTextNodeIndices(parentNode: Node, boundTextNodes: Map<
|
|||
resultCallback(node, j, boundTextNodes.get(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function prependAll(parentNode: Node, nodes: Node[]) {
|
||||
var lastInsertedNode = null;
|
||||
nodes.forEach(node => {
|
||||
if (isBlank(lastInsertedNode)) {
|
||||
var firstChild = DOM.firstChild(parentNode);
|
||||
if (isPresent(firstChild)) {
|
||||
DOM.insertBefore(firstChild, node);
|
||||
} else {
|
||||
DOM.appendChild(parentNode, node);
|
||||
}
|
||||
} else {
|
||||
DOM.insertAfter(lastInsertedNode, node);
|
||||
}
|
||||
lastInsertedNode = node;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DomElementBinder} from './element_binder';
|
||||
import {RenderProtoViewRef, ViewType} from '../../api';
|
||||
import {RenderProtoViewRef, ViewType, ViewEncapsulation} from '../../api';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
|
@ -14,9 +14,10 @@ export class DomProtoViewRef extends RenderProtoViewRef {
|
|||
}
|
||||
|
||||
export class DomProtoView {
|
||||
static create(type: ViewType, rootElement: Element, fragmentsRootNodeCount: number[],
|
||||
rootTextNodeIndices: number[],
|
||||
elementBinders: List<DomElementBinder>): DomProtoView {
|
||||
static create(type: ViewType, rootElement: Element, viewEncapsulation: ViewEncapsulation,
|
||||
fragmentsRootNodeCount: number[], rootTextNodeIndices: number[],
|
||||
elementBinders: List<DomElementBinder>,
|
||||
hostAttributes: Map<string, string>): DomProtoView {
|
||||
var boundTextNodeCount = rootTextNodeIndices.length;
|
||||
for (var i = 0; i < elementBinders.length; i++) {
|
||||
boundTextNodeCount += elementBinders[i].textNodeIndices.length;
|
||||
|
@ -24,12 +25,15 @@ export class DomProtoView {
|
|||
var isSingleElementFragment = fragmentsRootNodeCount.length === 1 &&
|
||||
fragmentsRootNodeCount[0] === 1 &&
|
||||
DOM.isElementNode(DOM.firstChild(DOM.content(rootElement)));
|
||||
return new DomProtoView(type, rootElement, elementBinders, rootTextNodeIndices,
|
||||
boundTextNodeCount, fragmentsRootNodeCount, isSingleElementFragment);
|
||||
return new DomProtoView(type, rootElement, viewEncapsulation, elementBinders, hostAttributes,
|
||||
rootTextNodeIndices, boundTextNodeCount, fragmentsRootNodeCount,
|
||||
isSingleElementFragment);
|
||||
}
|
||||
|
||||
constructor(public type: ViewType, public rootElement: Element,
|
||||
public elementBinders: List<DomElementBinder>, public rootTextNodeIndices: number[],
|
||||
public encapsulation: ViewEncapsulation,
|
||||
public elementBinders: List<DomElementBinder>,
|
||||
public hostAttributes: Map<string, string>, public rootTextNodeIndices: number[],
|
||||
public boundTextNodeCount: number, public fragmentsRootNodeCount: number[],
|
||||
public isSingleElementFragment: boolean) {}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,10 @@ export class ProtoViewBuilder {
|
|||
elements: List<ElementBinderBuilder> = [];
|
||||
rootTextBindings: Map<Node, ASTWithSource> = new Map();
|
||||
ngContentCount: number = 0;
|
||||
hostAttributes: Map<string, string> = new Map();
|
||||
|
||||
constructor(public rootElement, public type: api.ViewType,
|
||||
public useNativeShadowDom: boolean = false) {}
|
||||
public viewEncapsulation: api.ViewEncapsulation) {}
|
||||
|
||||
bindElement(element: HTMLElement, description: string = null): ElementBinderBuilder {
|
||||
var builder = new ElementBinderBuilder(this.elements.length, element, description);
|
||||
|
@ -65,6 +66,8 @@ export class ProtoViewBuilder {
|
|||
|
||||
bindNgContent() { this.ngContentCount++; }
|
||||
|
||||
setHostAttribute(name: string, value: string) { this.hostAttributes.set(name, value); }
|
||||
|
||||
build(): api.ProtoViewDto {
|
||||
var domElementBinders = [];
|
||||
|
||||
|
@ -119,7 +122,7 @@ export class ProtoViewBuilder {
|
|||
domElementBinders.push(new DomElementBinder({
|
||||
textNodeIndices: textNodeIndices,
|
||||
hasNestedProtoView: isPresent(nestedProtoView) || isPresent(ebb.componentId),
|
||||
hasNativeShadowRoot: isPresent(ebb.componentId) && this.useNativeShadowDom,
|
||||
hasNativeShadowRoot: false,
|
||||
eventLocals: new LiteralArray(ebb.eventBuilder.buildEventLocals()),
|
||||
localEvents: ebb.eventBuilder.buildLocalEvents(),
|
||||
globalEvents: ebb.eventBuilder.buildGlobalEvents()
|
||||
|
@ -127,8 +130,9 @@ export class ProtoViewBuilder {
|
|||
});
|
||||
var rootNodeCount = DOM.childNodes(DOM.content(this.rootElement)).length;
|
||||
return new api.ProtoViewDto({
|
||||
render: new DomProtoViewRef(DomProtoView.create(this.type, this.rootElement, [rootNodeCount],
|
||||
rootTextNodeIndices, domElementBinders)),
|
||||
render: new DomProtoViewRef(
|
||||
DomProtoView.create(this.type, this.rootElement, this.viewEncapsulation, [rootNodeCount],
|
||||
rootTextNodeIndices, domElementBinders, this.hostAttributes)),
|
||||
type: this.type,
|
||||
elementBinders: apiElementBinders,
|
||||
variableBindings: this.variableBindings,
|
||||
|
@ -178,7 +182,8 @@ export class ElementBinderBuilder {
|
|||
if (isPresent(this.nestedProtoView)) {
|
||||
throw new BaseException('Only one nested view per element is allowed');
|
||||
}
|
||||
this.nestedProtoView = new ProtoViewBuilder(rootElement, api.ViewType.EMBEDDED);
|
||||
this.nestedProtoView =
|
||||
new ProtoViewBuilder(rootElement, api.ViewType.EMBEDDED, api.ViewEncapsulation.NONE);
|
||||
return this.nestedProtoView;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {isPresent, isBlank, BaseException, isArray} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ListWrapper, SetWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
|
||||
import {DomElementBinder} from './element_binder';
|
||||
import {RenderProtoViewMergeMapping, RenderProtoViewRef, ViewType} from '../../api';
|
||||
import {
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderProtoViewRef,
|
||||
ViewType,
|
||||
ViewEncapsulation
|
||||
} from '../../api';
|
||||
import {
|
||||
NG_BINDING_CLASS,
|
||||
NG_CONTENT_ELEMENT_NAME,
|
||||
|
@ -12,7 +17,9 @@ import {
|
|||
cloneAndQueryProtoView,
|
||||
queryBoundElements,
|
||||
queryBoundTextNodeIndices,
|
||||
NG_SHADOW_ROOT_ELEMENT_NAME
|
||||
NG_SHADOW_ROOT_ELEMENT_NAME,
|
||||
isElementWithTag,
|
||||
prependAll
|
||||
} from '../util';
|
||||
|
||||
export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRef | List<any>>):
|
||||
|
@ -26,7 +33,9 @@ export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRe
|
|||
// modify the DOM
|
||||
mergeEmbeddedPvsIntoComponentOrRootPv(clonedProtoViews, hostViewAndBinderIndices);
|
||||
var fragments = [];
|
||||
mergeComponents(clonedProtoViews, hostViewAndBinderIndices, fragments);
|
||||
var elementsWithNativeShadowRoot: Set<Element> = new Set();
|
||||
mergeComponents(clonedProtoViews, hostViewAndBinderIndices, fragments,
|
||||
elementsWithNativeShadowRoot);
|
||||
// Note: Need to remark parent elements of bound text nodes
|
||||
// so that we can find them later via queryBoundElements!
|
||||
markBoundTextNodeParentsAsBoundElements(clonedProtoViews);
|
||||
|
@ -42,8 +51,9 @@ export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRe
|
|||
var boundTextNodeMap: Map<Node, any> = indexBoundTextNodes(clonedProtoViews);
|
||||
var rootTextNodeIndices =
|
||||
calcRootTextNodeIndices(rootNode, boundTextNodeMap, mergedBoundTextIndices);
|
||||
var mergedElementBinders = calcElementBinders(clonedProtoViews, mergedBoundElements,
|
||||
boundTextNodeMap, mergedBoundTextIndices);
|
||||
var mergedElementBinders =
|
||||
calcElementBinders(clonedProtoViews, mergedBoundElements, elementsWithNativeShadowRoot,
|
||||
boundTextNodeMap, mergedBoundTextIndices);
|
||||
|
||||
// create element / text index mappings
|
||||
var mappedElementIndices = calcMappedElementIndices(clonedProtoViews, mergedBoundElements);
|
||||
|
@ -53,9 +63,9 @@ export function mergeProtoViewsRecursively(protoViewRefs: List<RenderProtoViewRe
|
|||
var hostElementIndicesByViewIndex =
|
||||
calcHostElementIndicesByViewIndex(clonedProtoViews, hostViewAndBinderIndices);
|
||||
var nestedViewCounts = calcNestedViewCounts(hostViewAndBinderIndices);
|
||||
var mergedProtoView =
|
||||
DomProtoView.create(mainProtoView.original.type, rootElement, fragmentsRootNodeCount,
|
||||
rootTextNodeIndices, mergedElementBinders);
|
||||
var mergedProtoView = DomProtoView.create(
|
||||
mainProtoView.original.type, rootElement, mainProtoView.original.encapsulation,
|
||||
fragmentsRootNodeCount, rootTextNodeIndices, mergedElementBinders, new Map());
|
||||
return new RenderProtoViewMergeMapping(new DomProtoViewRef(mergedProtoView),
|
||||
fragmentsRootNodeCount.length, mappedElementIndices,
|
||||
mergedBoundElements.length, mappedTextIndices,
|
||||
|
@ -143,7 +153,8 @@ function calcNearestHostComponentOrRootPvIndices(clonedProtoViews: ClonedProtoVi
|
|||
}
|
||||
|
||||
function mergeComponents(clonedProtoViews: ClonedProtoView[], hostViewAndBinderIndices: number[][],
|
||||
targetFragments: Node[][]) {
|
||||
targetFragments: Node[][],
|
||||
targetElementsWithNativeShadowRoot: Set<Element>) {
|
||||
var hostProtoView = clonedProtoViews[0];
|
||||
hostProtoView.fragments.forEach((fragment) => targetFragments.push(fragment));
|
||||
|
||||
|
@ -153,13 +164,15 @@ function mergeComponents(clonedProtoViews: ClonedProtoView[], hostViewAndBinderI
|
|||
var hostProtoView = clonedProtoViews[hostViewIdx];
|
||||
var clonedProtoView = clonedProtoViews[viewIdx];
|
||||
if (clonedProtoView.original.type === ViewType.COMPONENT) {
|
||||
mergeComponent(hostProtoView, hostBinderIdx, clonedProtoView, targetFragments);
|
||||
mergeComponent(hostProtoView, hostBinderIdx, clonedProtoView, targetFragments,
|
||||
targetElementsWithNativeShadowRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeComponent(hostProtoView: ClonedProtoView, binderIdx: number,
|
||||
nestedProtoView: ClonedProtoView, targetFragments: Node[][]) {
|
||||
nestedProtoView: ClonedProtoView, targetFragments: Node[][],
|
||||
targetElementsWithNativeShadowRoot: Set<Element>) {
|
||||
var hostElement = hostProtoView.boundElements[binderIdx];
|
||||
|
||||
// We wrap the fragments into elements so that we can expand <ng-content>
|
||||
|
@ -176,8 +189,14 @@ function mergeComponent(hostProtoView: ClonedProtoView, binderIdx: number,
|
|||
|
||||
// unwrap the fragment elements into arrays of nodes after projecting
|
||||
var fragments = extractFragmentNodesFromElements(fragmentElements);
|
||||
appendComponentNodesToHost(hostProtoView, binderIdx, fragments[0]);
|
||||
|
||||
var useNativeShadowRoot = nestedProtoView.original.encapsulation === ViewEncapsulation.NATIVE;
|
||||
if (useNativeShadowRoot) {
|
||||
targetElementsWithNativeShadowRoot.add(hostElement);
|
||||
}
|
||||
MapWrapper.forEach(nestedProtoView.original.hostAttributes, (attrValue, attrName) => {
|
||||
DOM.setAttribute(hostElement, attrName, attrValue);
|
||||
});
|
||||
appendComponentNodesToHost(hostProtoView, binderIdx, fragments[0], useNativeShadowRoot);
|
||||
for (var i = 1; i < fragments.length; i++) {
|
||||
targetFragments.push(fragments[i]);
|
||||
}
|
||||
|
@ -209,10 +228,9 @@ function findContentElements(fragmentElements: Element[]): Element[] {
|
|||
}
|
||||
|
||||
function appendComponentNodesToHost(hostProtoView: ClonedProtoView, binderIdx: number,
|
||||
componentRootNodes: Node[]) {
|
||||
componentRootNodes: Node[], useNativeShadowRoot: boolean) {
|
||||
var hostElement = hostProtoView.boundElements[binderIdx];
|
||||
var binder = hostProtoView.original.elementBinders[binderIdx];
|
||||
if (binder.hasNativeShadowRoot) {
|
||||
if (useNativeShadowRoot) {
|
||||
var shadowRootWrapper = DOM.createElement(NG_SHADOW_ROOT_ELEMENT_NAME);
|
||||
for (var i = 0; i < componentRootNodes.length; i++) {
|
||||
DOM.appendChild(shadowRootWrapper, componentRootNodes[i]);
|
||||
|
@ -298,6 +316,7 @@ function calcRootTextNodeIndices(rootNode: Node, boundTextNodes: Map<Node, any>,
|
|||
}
|
||||
|
||||
function calcElementBinders(clonedProtoViews: ClonedProtoView[], mergedBoundElements: Element[],
|
||||
elementsWithNativeShadowRoot: Set<Element>,
|
||||
boundTextNodes: Map<Node, any>,
|
||||
targetBoundTextIndices: Map<Node, number>): DomElementBinder[] {
|
||||
var elementBinderByElement: Map<Element, DomElementBinder> =
|
||||
|
@ -311,7 +330,8 @@ function calcElementBinders(clonedProtoViews: ClonedProtoView[], mergedBoundElem
|
|||
targetBoundTextIndices.set(textNode, targetBoundTextIndices.size);
|
||||
});
|
||||
mergedElementBinders.push(
|
||||
updateElementBinderTextNodeIndices(elementBinderByElement.get(element), textNodeIndices));
|
||||
updateElementBinders(elementBinderByElement.get(element), textNodeIndices,
|
||||
SetWrapper.has(elementsWithNativeShadowRoot, element)));
|
||||
}
|
||||
return mergedElementBinders;
|
||||
}
|
||||
|
@ -330,8 +350,8 @@ function indexElementBindersByElement(mergableProtoViews: ClonedProtoView[]):
|
|||
return elementBinderByElement;
|
||||
}
|
||||
|
||||
function updateElementBinderTextNodeIndices(elementBinder: DomElementBinder,
|
||||
textNodeIndices: number[]): DomElementBinder {
|
||||
function updateElementBinders(elementBinder: DomElementBinder, textNodeIndices: number[],
|
||||
hasNativeShadowRoot: boolean): DomElementBinder {
|
||||
var result;
|
||||
if (isBlank(elementBinder)) {
|
||||
result = new DomElementBinder({
|
||||
|
@ -349,7 +369,7 @@ function updateElementBinderTextNodeIndices(elementBinder: DomElementBinder,
|
|||
eventLocals: elementBinder.eventLocals,
|
||||
localEvents: elementBinder.localEvents,
|
||||
globalEvents: elementBinder.globalEvents,
|
||||
hasNativeShadowRoot: elementBinder.hasNativeShadowRoot
|
||||
hasNativeShadowRoot: hasNativeShadowRoot
|
||||
});
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Inject, Injectable} from 'angular2/di';
|
||||
import {SetWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOCUMENT_TOKEN} from '../dom_tokens';
|
||||
|
||||
@Injectable()
|
||||
export class SharedStylesHost {
|
||||
protected _styles: string[] = [];
|
||||
protected _stylesSet: Set<string> = new Set();
|
||||
|
||||
constructor() {}
|
||||
|
||||
addStyles(styles: string[]) {
|
||||
var additions = [];
|
||||
styles.forEach(style => {
|
||||
if (!SetWrapper.has(this._stylesSet, style)) {
|
||||
this._stylesSet.add(style);
|
||||
this._styles.push(style);
|
||||
additions.push(style);
|
||||
}
|
||||
});
|
||||
this.onStylesAdded(additions);
|
||||
}
|
||||
|
||||
protected onStylesAdded(additions: string[]) {}
|
||||
|
||||
getAllStyles(): string[] { return this._styles; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomSharedStylesHost extends SharedStylesHost {
|
||||
private _hostNodes: Set<Node> = new Set();
|
||||
constructor(@Inject(DOCUMENT_TOKEN) doc: any) {
|
||||
super();
|
||||
this._hostNodes.add(doc.head);
|
||||
}
|
||||
_addStylesToHost(styles: string[], host: Node) {
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
var style = styles[i];
|
||||
DOM.appendChild(host, DOM.createStyleElement(style));
|
||||
}
|
||||
}
|
||||
addHost(hostNode: Node) {
|
||||
this._addStylesToHost(this._styles, hostNode);
|
||||
this._hostNodes.add(hostNode);
|
||||
}
|
||||
removeHost(hostNode: Node) { SetWrapper.delete(this._hostNodes, hostNode); }
|
||||
|
||||
onStylesAdded(additions: string[]) {
|
||||
this._hostNodes.forEach((hostNode) => { this._addStylesToHost(additions, hostNode); });
|
||||
}
|
||||
}
|
|
@ -5,9 +5,8 @@
|
|||
*/
|
||||
|
||||
export * from './dom/compiler/view_loader';
|
||||
export * from './dom/view/shared_styles_host';
|
||||
export * from './dom/compiler/compiler';
|
||||
export * from './dom/dom_renderer';
|
||||
export * from './dom/shadow_dom/shadow_dom_strategy';
|
||||
export * from './dom/shadow_dom/native_shadow_dom_strategy';
|
||||
export * from './dom/shadow_dom/emulated_scoped_shadow_dom_strategy';
|
||||
export * from './dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
export * from './dom/dom_tokens';
|
||||
export * from './api';
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
|
||||
import {el} from './utils';
|
||||
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/render';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DebugElement} from 'angular2/src/debug/debug_element';
|
||||
|
|
|
@ -15,10 +15,6 @@ import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
|||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {
|
||||
EmulatedUnscopedShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
@ -54,9 +50,12 @@ import {RenderCompiler, Renderer} from 'angular2/src/render/api';
|
|||
import {
|
||||
DomRenderer,
|
||||
DOCUMENT_TOKEN,
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||
} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||
DefaultDomCompiler,
|
||||
APP_ID_TOKEN,
|
||||
SharedStylesHost,
|
||||
DomSharedStylesHost
|
||||
} from 'angular2/src/render/render';
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
import {Log} from './utils';
|
||||
|
||||
|
@ -94,13 +93,14 @@ function _getAppBindings() {
|
|||
return [
|
||||
bind(DOCUMENT_TOKEN)
|
||||
.toValue(appDoc),
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
bind(APP_ID_TOKEN).toValue('a'),
|
||||
DefaultDomCompiler,
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
DomSharedStylesHost,
|
||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||
ProtoViewFactory,
|
||||
AppViewPool,
|
||||
AppViewManager,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:async';
|
|||
import 'package:angular2/src/change_detection/parser/lexer.dart' as ng;
|
||||
import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
|
||||
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
|
||||
import 'package:angular2/src/dom/dom_adapter.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/compile_pipeline.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/style_inliner.dart';
|
||||
|
@ -96,7 +97,7 @@ class _TemplateExtractor {
|
|||
// Check for "imperative views".
|
||||
if (viewDef.template == null && viewDef.templateAbsUrl == null) return null;
|
||||
|
||||
var templateEl = await _loader.load(viewDef);
|
||||
var templateAndStyles = await _loader.load(viewDef);
|
||||
|
||||
// NOTE(kegluneq): Since this is a global, we must not have any async
|
||||
// operations between saving and restoring it, otherwise we can get into
|
||||
|
@ -108,7 +109,7 @@ class _TemplateExtractor {
|
|||
var pipeline = new CompilePipeline(_factory.createSteps(viewDef));
|
||||
|
||||
var compileElements =
|
||||
pipeline.process(templateEl, ViewType.COMPONENT, viewDef.componentId);
|
||||
pipeline.processElements(DOM.createTemplate(templateAndStyles.template), ViewType.COMPONENT, viewDef);
|
||||
var protoViewDto = compileElements[0].inheritedProtoView.build();
|
||||
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
|
|
|
@ -213,7 +213,8 @@ export class Serializer {
|
|||
'template': view.template,
|
||||
'directives': this.serialize(view.directives, DirectiveMetadata),
|
||||
'styleAbsUrls': view.styleAbsUrls,
|
||||
'styles': view.styles
|
||||
'styles': view.styles,
|
||||
'encapsulation': view.encapsulation
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -223,7 +224,8 @@ export class Serializer {
|
|||
templateAbsUrl: obj['templateAbsUrl'], template: obj['template'],
|
||||
directives: this.deserialize(obj['directives'], DirectiveMetadata),
|
||||
styleAbsUrls: obj['styleAbsUrls'],
|
||||
styles: obj['styles']
|
||||
styles: obj['styles'],
|
||||
encapsulation: obj['encapsulation']
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,15 +24,16 @@ import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
|||
import {
|
||||
DomRenderer,
|
||||
DOCUMENT_TOKEN,
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
|
||||
} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
|
||||
DefaultDomCompiler,
|
||||
APP_ID_RANDOM_BINDING
|
||||
} from 'angular2/src/render/render';
|
||||
import {
|
||||
SharedStylesHost,
|
||||
DomSharedStylesHost
|
||||
} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {
|
||||
EmulatedUnscopedShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||
|
@ -89,14 +90,15 @@ function _injectorBindings(): List<Type | Binding | List<any>> {
|
|||
return new EventManager(plugins, ngZone);
|
||||
},
|
||||
[NgZone]),
|
||||
bind(ShadowDomStrategy)
|
||||
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
|
||||
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
Serializer,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
APP_ID_RANDOM_BINDING,
|
||||
DefaultDomCompiler,
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
DomSharedStylesHost,
|
||||
bind(SharedStylesHost).toAlias(DomSharedStylesHost),
|
||||
Serializer,
|
||||
bind(ON_WEBWORKER).toValue(false),
|
||||
RenderViewWithFragmentsStore,
|
||||
RenderProtoViewRefStore,
|
||||
|
|
|
@ -20,7 +20,7 @@ import {bind, Inject, Injector} from 'angular2/di';
|
|||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/render';
|
||||
|
||||
@Component({selector: 'hello-app'})
|
||||
@View({template: '{{greeting}} world!'})
|
||||
|
|
|
@ -24,7 +24,7 @@ import {Component, View, LifecycleEvent} from 'angular2/annotations';
|
|||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/render';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
|
|
|
@ -1452,7 +1452,7 @@ class MyService {
|
|||
}
|
||||
|
||||
@Component({selector: 'simple-imp-cmp'})
|
||||
@View({renderer: 'simple-imp-cmp-renderer', template: ''})
|
||||
@View({template: ''})
|
||||
@Injectable()
|
||||
class SimpleImperativeViewComponent {
|
||||
done;
|
||||
|
|
|
@ -34,9 +34,9 @@ import {
|
|||
ViewContainerRef,
|
||||
ElementRef,
|
||||
TemplateRef,
|
||||
bind
|
||||
bind,
|
||||
ViewEncapsulation
|
||||
} from 'angular2/angular2';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/render/render';
|
||||
|
||||
export function main() {
|
||||
describe('projection', () => {
|
||||
|
@ -399,27 +399,21 @@ export function main() {
|
|||
}));
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
it('should support native content projection',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<simple-native>' +
|
||||
'<div>A</div>' +
|
||||
'</simple-native>',
|
||||
directives: [SimpleNative]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
it('should support native content projection',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<simple-native>' +
|
||||
'<div>A</div>' +
|
||||
'</simple-native>',
|
||||
directives: [SimpleNative]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
expect(main.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -438,7 +432,11 @@ class Simple {
|
|||
}
|
||||
|
||||
@Component({selector: 'simple-native'})
|
||||
@View({template: 'SIMPLE(<content></content>)', directives: []})
|
||||
@View({
|
||||
template: 'SIMPLE(<content></content>)',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NATIVE
|
||||
})
|
||||
class SimpleNative {
|
||||
}
|
||||
|
||||
|
|
|
@ -17,26 +17,38 @@ import {Type, isBlank, stringify, isPresent, BaseException} from 'angular2/src/f
|
|||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {DomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {ProtoViewDto, ViewDefinition, DirectiveMetadata, ViewType} from 'angular2/src/render/api';
|
||||
import {CompileElement} from 'angular2/src/render/dom/compiler/compile_element';
|
||||
import {
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
DirectiveMetadata,
|
||||
ViewType,
|
||||
ViewEncapsulation
|
||||
} from 'angular2/src/render/api';
|
||||
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
|
||||
import {CompileStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory';
|
||||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||
|
||||
import {MockStep} from './pipeline_spec';
|
||||
|
||||
export function runCompilerCommonTests() {
|
||||
describe('DomCompiler', function() {
|
||||
var mockStepFactory: MockStepFactory;
|
||||
var sharedStylesHost: SharedStylesHost;
|
||||
|
||||
function createCompiler(processClosure, urlData = null) {
|
||||
beforeEach(() => {sharedStylesHost = new SharedStylesHost()});
|
||||
|
||||
function createCompiler(processElementClosure = null, processStyleClosure = null,
|
||||
urlData = null) {
|
||||
if (isBlank(urlData)) {
|
||||
urlData = new Map();
|
||||
}
|
||||
var tplLoader = new FakeViewLoader(urlData);
|
||||
mockStepFactory = new MockStepFactory([new MockStep(processClosure)]);
|
||||
return new DomCompiler(mockStepFactory, tplLoader, false);
|
||||
mockStepFactory =
|
||||
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
|
||||
return new DomCompiler(mockStepFactory, tplLoader, sharedStylesHost);
|
||||
}
|
||||
|
||||
describe('compile', () => {
|
||||
|
@ -61,12 +73,13 @@ export function runCompilerCommonTests() {
|
|||
});
|
||||
|
||||
var dirMetadata = DirectiveMetadata.create(
|
||||
{id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
{id: 'id', selector: 'custom', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
compiler.compileHost(dirMetadata)
|
||||
.then((protoView) => {
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(
|
||||
resolveInternalDomProtoView(protoView.render).rootElement))))
|
||||
.toEqual('CUSTOM');
|
||||
resolveInternalDomProtoView(protoView.render).rootElement)))
|
||||
.toLowerCase())
|
||||
.toEqual('custom');
|
||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
||||
expect(protoView.variableBindings)
|
||||
.toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
||||
|
@ -88,7 +101,7 @@ export function runCompilerCommonTests() {
|
|||
|
||||
it('should load url templates', inject([AsyncTestCompleter], (async) => {
|
||||
var urlData = MapWrapper.createFromStringMap({'someUrl': 'url component'});
|
||||
var compiler = createCompiler(EMPTY_STEP, urlData);
|
||||
var compiler = createCompiler(EMPTY_STEP, null, urlData);
|
||||
compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
|
||||
.then((protoView) => {
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).rootElement))
|
||||
|
@ -98,7 +111,7 @@ export function runCompilerCommonTests() {
|
|||
}));
|
||||
|
||||
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler(EMPTY_STEP, new Map());
|
||||
var compiler = createCompiler(EMPTY_STEP, null, new Map());
|
||||
PromiseWrapper.catchError(
|
||||
compiler.compile(
|
||||
new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'})),
|
||||
|
@ -137,6 +150,110 @@ export function runCompilerCommonTests() {
|
|||
|
||||
});
|
||||
|
||||
describe('compile styles', () => {
|
||||
it('should run the steps', inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler(null, (style) => { return style + 'b {};'; });
|
||||
compiler.compile(new ViewDefinition(
|
||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
||||
.then((protoViewDto) => {
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};b {};']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should store the styles in the SharedStylesHost for ViewEncapsulation.NONE',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
styles: ['a {};'],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement)).toEqual('');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should store the styles in the SharedStylesHost for ViewEncapsulation.EMULATED',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
styles: ['a {};'],
|
||||
encapsulation: ViewEncapsulation.EMULATED
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement)).toEqual('');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
it('should store the styles in the template for ViewEncapsulation.NATIVE',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
styles: ['a {};'],
|
||||
encapsulation: ViewEncapsulation.NATIVE
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement))
|
||||
.toEqual('<style>a {};</style>');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
it('should default to ViewEncapsulation.NONE if no styles are specified',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(
|
||||
new ViewDefinition({componentId: 'someComponent', template: '', styles: []}))
|
||||
.then((protoView) => {
|
||||
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.NONE);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should default to ViewEncapsulation.EMULATED if styles are specified',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(new ViewDefinition(
|
||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
||||
.then((protoView) => {
|
||||
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.EMULATED);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('mergeProtoViews', () => {
|
||||
it('should store the styles of the merged ProtoView in the SharedStylesHost',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var compiler = createCompiler();
|
||||
compiler.compile(new ViewDefinition(
|
||||
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
||||
.then(protoViewDto => compiler.mergeProtoViewsRecursively([protoViewDto.render]))
|
||||
.then(_ => {
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -155,14 +272,6 @@ class MockStepFactory extends CompileStepFactory {
|
|||
}
|
||||
}
|
||||
|
||||
class MockStep implements CompileStep {
|
||||
processClosure: Function;
|
||||
constructor(process) { this.processClosure = process; }
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
||||
var EMPTY_STEP = (parent, current, control) => {
|
||||
if (isPresent(parent)) {
|
||||
current.inheritedProtoView = parent.inheritedProtoView;
|
||||
|
@ -176,16 +285,17 @@ class FakeViewLoader extends ViewLoader {
|
|||
this._urlData = urlData;
|
||||
}
|
||||
|
||||
load(view: ViewDefinition): Promise<any> {
|
||||
if (isPresent(view.template)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(view.template));
|
||||
load(viewDef): Promise<any> {
|
||||
var styles = isPresent(viewDef.styles) ? viewDef.styles : [];
|
||||
if (isPresent(viewDef.template)) {
|
||||
return PromiseWrapper.resolve(new TemplateAndStyles(viewDef.template, styles));
|
||||
}
|
||||
|
||||
if (isPresent(view.templateAbsUrl)) {
|
||||
var content = this._urlData.get(view.templateAbsUrl);
|
||||
if (isPresent(viewDef.templateAbsUrl)) {
|
||||
var content = this._urlData.get(viewDef.templateAbsUrl);
|
||||
return isPresent(content) ?
|
||||
PromiseWrapper.resolve(DOM.createTemplate(content)) :
|
||||
PromiseWrapper.reject(`Failed to fetch url "${view.templateAbsUrl}"`, null);
|
||||
PromiseWrapper.resolve(new TemplateAndStyles(content, styles)) :
|
||||
PromiseWrapper.reject(`Failed to fetch url "${viewDef.templateAbsUrl}"`, null);
|
||||
}
|
||||
|
||||
throw new BaseException('View should have either the templateUrl or template property set');
|
||||
|
|
|
@ -4,12 +4,10 @@ import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/col
|
|||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {DirectiveParser} from 'angular2/src/render/dom/compiler/directive_parser';
|
||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
|
||||
import {CompileElement} from 'angular2/src/render/dom/compiler/compile_element';
|
||||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||
import {ViewDefinition, DirectiveMetadata} from 'angular2/src/render/api';
|
||||
import {ViewDefinition, DirectiveMetadata, ViewType} from 'angular2/src/render/api';
|
||||
import {Lexer, Parser} from 'angular2/src/change_detection/change_detection';
|
||||
import {ElementBinderBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {MockStep} from './pipeline_spec';
|
||||
|
||||
export function main() {
|
||||
describe('DirectiveParser', () => {
|
||||
|
@ -47,9 +45,15 @@ export function main() {
|
|||
]);
|
||||
}
|
||||
|
||||
function createViewDefinition(): ViewDefinition {
|
||||
return new ViewDefinition({componentId: 'someComponent'});
|
||||
}
|
||||
|
||||
function process(el, propertyBindings = null, directives = null): List<ElementBinderBuilder> {
|
||||
var pipeline = createPipeline(propertyBindings, directives);
|
||||
return ListWrapper.map(pipeline.process(el), (ce) => ce.inheritedElementBinder);
|
||||
return ListWrapper.map(
|
||||
pipeline.processElements(el, ViewType.COMPONENT, createViewDefinition()),
|
||||
(ce) => ce.inheritedElementBinder);
|
||||
}
|
||||
|
||||
it('should not add directives if they are not used', () => {
|
||||
|
@ -70,12 +74,14 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should compile children by default', () => {
|
||||
var results = createPipeline().process(el('<div some-decor></div>'));
|
||||
var results = createPipeline().processElements(el('<div some-decor></div>'),
|
||||
ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].compileChildren).toEqual(true);
|
||||
});
|
||||
|
||||
it('should stop compiling children when specified in the directive config', () => {
|
||||
var results = createPipeline().process(el('<div some-decor-ignoring-children></div>'));
|
||||
var results = createPipeline().processElements(el('<div some-decor-ignoring-children></div>'),
|
||||
ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].compileChildren).toEqual(false);
|
||||
});
|
||||
|
||||
|
@ -191,14 +197,6 @@ export function main() {
|
|||
});
|
||||
}
|
||||
|
||||
class MockStep implements CompileStep {
|
||||
processClosure: Function;
|
||||
constructor(process) { this.processClosure = process; }
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
||||
var someComponent = DirectiveMetadata.create(
|
||||
{selector: 'some-comp', id: 'someComponent', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
|
|
|
@ -9,16 +9,21 @@ import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
|
|||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||
|
||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ProtoViewDto, ViewType} from 'angular2/src/render/api';
|
||||
import {ProtoViewDto, ViewType, ViewEncapsulation, ViewDefinition} from 'angular2/src/render/api';
|
||||
|
||||
export function main() {
|
||||
describe('compile_pipeline', () => {
|
||||
function createViewDefinition(): ViewDefinition {
|
||||
return new ViewDefinition({componentId: 'someComponent'});
|
||||
}
|
||||
|
||||
describe('children compilation', () => {
|
||||
it('should walk the tree in depth first order including template contents', () => {
|
||||
var element = el('<div id="1"><template id="2"><span id="3"></span></template></div>');
|
||||
|
||||
var step0Log = [];
|
||||
var results = new CompilePipeline([createLoggerStep(step0Log)]).process(element);
|
||||
var results = new CompilePipeline([createLoggerStep(step0Log)])
|
||||
.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
|
||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
||||
expect(resultIdLog(results)).toEqual(['1', '2', '3']);
|
||||
|
@ -30,7 +35,7 @@ export function main() {
|
|||
|
||||
var step0Log = [];
|
||||
var pipeline = new CompilePipeline([new IgnoreChildrenStep(), createLoggerStep(step0Log)]);
|
||||
var results = pipeline.process(element);
|
||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
|
||||
expect(step0Log).toEqual(['1', '1<2']);
|
||||
expect(resultIdLog(results)).toEqual(['1', '2']);
|
||||
|
@ -42,11 +47,12 @@ export function main() {
|
|||
var pipeline = new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
|
||||
current.inheritedProtoView = new ProtoViewBuilder(current.element, ViewType.EMBEDDED);
|
||||
current.inheritedProtoView =
|
||||
new ProtoViewBuilder(current.element, ViewType.EMBEDDED, ViewEncapsulation.NONE);
|
||||
}
|
||||
})
|
||||
]);
|
||||
var results = pipeline.process(element);
|
||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].inheritedProtoView).toBe(results[1].inheritedProtoView);
|
||||
expect(results[2].inheritedProtoView).toBe(results[3].inheritedProtoView);
|
||||
});
|
||||
|
@ -60,14 +66,15 @@ export function main() {
|
|||
}
|
||||
})
|
||||
]);
|
||||
var results = pipeline.process(element);
|
||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].inheritedElementBinder).toBe(results[1].inheritedElementBinder);
|
||||
expect(results[2].inheritedElementBinder).toBe(results[3].inheritedElementBinder);
|
||||
});
|
||||
|
||||
it('should mark root elements as viewRoot', () => {
|
||||
var rootElement = el('<div></div>');
|
||||
var results = new CompilePipeline([]).process(rootElement);
|
||||
var results = new CompilePipeline([])
|
||||
.processElements(rootElement, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -80,7 +87,7 @@ export function main() {
|
|||
}
|
||||
})
|
||||
]);
|
||||
var results = pipeline.process(element);
|
||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].inheritedElementBinder.distanceToParent).toBe(0);
|
||||
expect(results[1].inheritedElementBinder.distanceToParent).toBe(1);
|
||||
expect(results[3].inheritedElementBinder.distanceToParent).toBe(2);
|
||||
|
@ -95,7 +102,7 @@ export function main() {
|
|||
new IgnoreCurrentElementStep(),
|
||||
createLoggerStep(logs),
|
||||
]);
|
||||
var results = pipeline.process(element);
|
||||
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
|
||||
expect(results.length).toBe(2);
|
||||
expect(logs).toEqual(['1', '1<3'])
|
||||
|
@ -108,7 +115,7 @@ export function main() {
|
|||
var step1Log = [];
|
||||
var pipeline =
|
||||
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
|
||||
var result = pipeline.process(element);
|
||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', '2', '3']);
|
||||
|
@ -125,7 +132,7 @@ export function main() {
|
|||
createWrapperStep('wrap1', step1Log),
|
||||
createLoggerStep(step2Log)
|
||||
]);
|
||||
var result = pipeline.process(element);
|
||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
||||
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap1#0', 'wrap1#0<2', '2<3']);
|
||||
|
@ -143,7 +150,7 @@ export function main() {
|
|||
createWrapperStep('wrap1', step1Log),
|
||||
createLoggerStep(step2Log)
|
||||
]);
|
||||
var result = pipeline.process(element);
|
||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
|
||||
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<wrap1#0', 'wrap1#0<3']);
|
||||
|
@ -156,7 +163,7 @@ export function main() {
|
|||
var step1Log = [];
|
||||
var pipeline =
|
||||
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
|
||||
var result = pipeline.process(element);
|
||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(step0Log).toEqual(['1', '1<2', '2<3']);
|
||||
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap0#1', 'wrap0#1<2', '2<3']);
|
||||
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', 'wrap0#1', '2', '3']);
|
||||
|
@ -177,40 +184,68 @@ export function main() {
|
|||
}),
|
||||
createLoggerStep(resultLog)
|
||||
]);
|
||||
var result = pipeline.process(element);
|
||||
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
|
||||
expect(result[2]).toBe(newChild);
|
||||
expect(resultLog).toEqual(['1', '1<2', '1<3']);
|
||||
expect(resultIdLog(result)).toEqual(['1', '2', '3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processStyles', () => {
|
||||
it('should call the steps for every style', () => {
|
||||
var stepCalls = [];
|
||||
var pipeline = new CompilePipeline([
|
||||
new MockStep(null,
|
||||
(style) => {
|
||||
stepCalls.push(style);
|
||||
return style;
|
||||
})
|
||||
]);
|
||||
var result = pipeline.processStyles(['a', 'b']);
|
||||
expect(result[0]).toEqual('a');
|
||||
expect(result[1]).toEqual('b');
|
||||
expect(result).toEqual(stepCalls);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockStep implements CompileStep {
|
||||
processClosure: Function;
|
||||
constructor(process) { this.processClosure = process; }
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
export class MockStep implements CompileStep {
|
||||
constructor(private processElementClosure: Function,
|
||||
private processStyleClosure: Function = null) {}
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
if (isPresent(this.processElementClosure)) {
|
||||
this.processElementClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
processStyle(style: string): string {
|
||||
if (isPresent(this.processStyleClosure)) {
|
||||
return this.processStyleClosure(style);
|
||||
} else {
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class IgnoreChildrenStep implements CompileStep {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attributeMap = DOM.attributeMap(current.element);
|
||||
if (attributeMap.has('ignore-children')) {
|
||||
current.compileChildren = false;
|
||||
}
|
||||
}
|
||||
processStyle(style: string): string { return style; }
|
||||
}
|
||||
|
||||
class IgnoreCurrentElementStep implements CompileStep {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attributeMap = DOM.attributeMap(current.element);
|
||||
if (attributeMap.has('ignore-current')) {
|
||||
control.ignoreCurrentElement();
|
||||
}
|
||||
}
|
||||
processStyle(style: string): string { return style; }
|
||||
}
|
||||
|
||||
function logEntry(log: string[], parent, current) {
|
||||
|
|
|
@ -3,11 +3,10 @@ import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
|||
import {PropertyBindingParser} from 'angular2/src/render/dom/compiler/property_binding_parser';
|
||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {CompileElement} from 'angular2/src/render/dom/compiler/compile_element';
|
||||
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
|
||||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||
import {Lexer, Parser} from 'angular2/src/change_detection/change_detection';
|
||||
import {ElementBinderBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ViewDefinition, ViewType} from 'angular2/src/render/api';
|
||||
import {MockStep} from './pipeline_spec';
|
||||
|
||||
var EMPTY_MAP = new Map();
|
||||
|
||||
|
@ -24,9 +23,15 @@ export function main() {
|
|||
]);
|
||||
}
|
||||
|
||||
function createViewDefinition(): ViewDefinition {
|
||||
return new ViewDefinition({componentId: 'someComponent'});
|
||||
}
|
||||
|
||||
function process(element, hasNestedProtoView = false): List<ElementBinderBuilder> {
|
||||
return ListWrapper.map(createPipeline(hasNestedProtoView).process(element),
|
||||
(compileElement) => compileElement.inheritedElementBinder);
|
||||
return ListWrapper.map(
|
||||
createPipeline(hasNestedProtoView)
|
||||
.processElements(element, ViewType.COMPONENT, createViewDefinition()),
|
||||
(compileElement) => compileElement.inheritedElementBinder);
|
||||
}
|
||||
|
||||
it('should detect [] syntax', () => {
|
||||
|
@ -174,13 +179,15 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should store bound properties as temporal attributes', () => {
|
||||
var results = createPipeline().process(el('<div bind-a="b" [c]="d"></div>'));
|
||||
var results = createPipeline().processElements(el('<div bind-a="b" [c]="d"></div>'),
|
||||
ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].attrs().get('a')).toEqual('b');
|
||||
expect(results[0].attrs().get('c')).toEqual('d');
|
||||
});
|
||||
|
||||
it('should store variables as temporal attributes', () => {
|
||||
var results = createPipeline().process(el('<div var-a="b" #c="d"></div>'));
|
||||
var results = createPipeline().processElements(el('<div var-a="b" #c="d"></div>'),
|
||||
ViewType.COMPONENT, createViewDefinition());
|
||||
expect(results[0].attrs().get('a')).toEqual('b');
|
||||
expect(results[0].attrs().get('c')).toEqual('d');
|
||||
});
|
||||
|
@ -210,11 +217,3 @@ export function main() {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
class MockStep implements CompileStep {
|
||||
processClosure: Function;
|
||||
constructor(process) { this.processClosure = process; }
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
el,
|
||||
normalizeCSS
|
||||
} from 'angular2/test_lib';
|
||||
import {ShadowCss} from 'angular2/src/render/dom/shadow_dom/shadow_css';
|
||||
import {ShadowCss} from 'angular2/src/render/dom/compiler/shadow_css';
|
||||
|
||||
import {RegExpWrapper, StringWrapper, isPresent} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
@ -0,0 +1,135 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
ProtoViewBuilder,
|
||||
ElementBinderBuilder
|
||||
} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ViewDefinition, ViewType, ViewEncapsulation} from 'angular2/src/render/api';
|
||||
|
||||
import {StyleEncapsulator} from 'angular2/src/render/dom/compiler/style_encapsulator';
|
||||
import {MockStep} from './pipeline_spec';
|
||||
|
||||
export function main() {
|
||||
describe('StyleEncapsulator', () => {
|
||||
var componentIdCache;
|
||||
|
||||
beforeEach(() => { componentIdCache = new Map(); });
|
||||
|
||||
function createPipeline(viewDef: ViewDefinition) {
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
var tagName = DOM.tagName(current.element).toLowerCase();
|
||||
if (tagName.startsWith('comp-')) {
|
||||
current.bindElement().setComponentId(tagName);
|
||||
}
|
||||
}),
|
||||
new StyleEncapsulator('someapp', viewDef, componentIdCache)
|
||||
]);
|
||||
}
|
||||
|
||||
function createViewDefinition(encapsulation: ViewEncapsulation, componentId: string):
|
||||
ViewDefinition {
|
||||
return new ViewDefinition({encapsulation: encapsulation, componentId: componentId});
|
||||
}
|
||||
|
||||
function processStyles(encapsulation: ViewEncapsulation, componentId: string, styles: string[]):
|
||||
string[] {
|
||||
var viewDef = createViewDefinition(encapsulation, componentId);
|
||||
return createPipeline(viewDef).processStyles(styles);
|
||||
}
|
||||
|
||||
function processElements(encapsulation: ViewEncapsulation, componentId: string,
|
||||
template: Element, viewType: ViewType = ViewType.COMPONENT):
|
||||
ProtoViewBuilder {
|
||||
var viewDef = createViewDefinition(encapsulation, componentId);
|
||||
var compileElements = createPipeline(viewDef).processElements(template, viewType, viewDef);
|
||||
return compileElements[0].inheritedProtoView;
|
||||
}
|
||||
|
||||
describe('ViewEncapsulation.NONE', () => {
|
||||
it('should not change the styles', () => {
|
||||
var cs = processStyles(ViewEncapsulation.NONE, 'someComponent', ['.one {}']);
|
||||
expect(cs[0]).toEqual('.one {}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ViewEncapsulation.NATIVE', () => {
|
||||
it('should not change the styles', () => {
|
||||
var cs = processStyles(ViewEncapsulation.NATIVE, 'someComponent', ['.one {}']);
|
||||
expect(cs[0]).toEqual('.one {}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ViewEncapsulation.EMULATED', () => {
|
||||
it('should scope styles', () => {
|
||||
var cs = processStyles(ViewEncapsulation.EMULATED, 'someComponent', ['.foo {} :host {}']);
|
||||
expect(cs[0]).toEqual(".foo[_ngcontent-someapp-0] {\n\n}\n\n[_nghost-someapp-0] {\n\n}");
|
||||
});
|
||||
|
||||
it('should return the same style given the same component', () => {
|
||||
var style = '.foo {} :host {}';
|
||||
var cs1 = processStyles(ViewEncapsulation.EMULATED, 'someComponent', [style]);
|
||||
var cs2 = processStyles(ViewEncapsulation.EMULATED, 'someComponent', [style]);
|
||||
|
||||
expect(cs1[0]).toEqual(cs2[0]);
|
||||
});
|
||||
|
||||
it('should return different styles given different components', () => {
|
||||
var style = '.foo {} :host {}';
|
||||
var cs1 = processStyles(ViewEncapsulation.EMULATED, 'someComponent1', [style]);
|
||||
var cs2 = processStyles(ViewEncapsulation.EMULATED, 'someComponent2', [style]);
|
||||
|
||||
expect(cs1[0]).not.toEqual(cs2[0]);
|
||||
});
|
||||
|
||||
it('should add a host attribute to component proto views', () => {
|
||||
var template = DOM.createTemplate('<div></div>');
|
||||
var protoViewBuilder =
|
||||
processElements(ViewEncapsulation.EMULATED, 'someComponent', template);
|
||||
expect(protoViewBuilder.hostAttributes.get('_nghost-someapp-0')).toEqual('');
|
||||
});
|
||||
|
||||
it('should not add a host attribute to embedded proto views', () => {
|
||||
var template = DOM.createTemplate('<div></div>');
|
||||
var protoViewBuilder = processElements(ViewEncapsulation.EMULATED, 'someComponent',
|
||||
template, ViewType.EMBEDDED);
|
||||
expect(protoViewBuilder.hostAttributes.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should not add a host attribute to host proto views', () => {
|
||||
var template = DOM.createTemplate('<div></div>');
|
||||
var protoViewBuilder =
|
||||
processElements(ViewEncapsulation.EMULATED, 'someComponent', template, ViewType.HOST);
|
||||
expect(protoViewBuilder.hostAttributes.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should add an attribute to the content elements', () => {
|
||||
var template = DOM.createTemplate('<div></div>');
|
||||
processElements(ViewEncapsulation.EMULATED, 'someComponent', template);
|
||||
expect(DOM.getInnerHTML(template)).toEqual('<div _ngcontent-someapp-0=""></div>');
|
||||
});
|
||||
|
||||
it('should not add an attribute to the content elements for host views', () => {
|
||||
var template = DOM.createTemplate('<div></div>');
|
||||
processElements(ViewEncapsulation.EMULATED, 'someComponent', template, ViewType.HOST);
|
||||
expect(DOM.getInnerHTML(template)).toEqual('<div></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -9,6 +9,7 @@ import {
|
|||
ElementBinderBuilder
|
||||
} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ViewDefinition, ViewType} from 'angular2/src/render/api';
|
||||
|
||||
export function main() {
|
||||
describe('TextInterpolationParser', () => {
|
||||
|
@ -17,8 +18,13 @@ export function main() {
|
|||
[new IgnoreChildrenStep(), new TextInterpolationParser(new Parser(new Lexer()))]);
|
||||
}
|
||||
|
||||
function createViewDefinition(): ViewDefinition {
|
||||
return new ViewDefinition({componentId: 'someComponent'});
|
||||
}
|
||||
|
||||
function process(templateString: string): ProtoViewBuilder {
|
||||
var compileElements = createPipeline().process(DOM.createTemplate(templateString));
|
||||
var compileElements = createPipeline().processElements(
|
||||
DOM.createTemplate(templateString), ViewType.COMPONENT, createViewDefinition());
|
||||
return compileElements[0].inheritedProtoView;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,21 +10,21 @@ import {
|
|||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import {ViewDefinition} from 'angular2/src/render/api';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {MockXHR} from 'angular2/src/render/xhr_mock';
|
||||
import {ViewDefinition} from 'angular2/src/render/api';
|
||||
|
||||
export function main() {
|
||||
describe('ViewLoader', () => {
|
||||
var loader, xhr, styleUrlResolver, urlResolver;
|
||||
var loader: ViewLoader;
|
||||
var xhr, styleUrlResolver, urlResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
xhr = new MockXHR();
|
||||
|
@ -36,32 +36,33 @@ export function main() {
|
|||
|
||||
describe('html', () => {
|
||||
it('should load inline templates', inject([AsyncTestCompleter], (async) => {
|
||||
var view = new ViewDefinition({template: 'template template'});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.content(el)).toHaveText('template template');
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({template: 'template template'}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('template template');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should load templates through XHR', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/foo.html', 'xhr template');
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.content(el)).toHaveText('xhr template');
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('xhr template');
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should resolve urls in styles', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/foo.html',
|
||||
'<style>.foo { background-image: url("double.jpg"); }</style>');
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.content(el))
|
||||
.toHaveText(".foo { background-image: url('http://ng.io/double.jpg'); }");
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('');
|
||||
expect(el.styles)
|
||||
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
|
@ -73,85 +74,66 @@ export function main() {
|
|||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
||||
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el)).toEqual("<style>/* foo.css */\n</style>");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return a new template element on each call',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var firstEl;
|
||||
// we have only one xhr.expect, so there can only be one xhr call!
|
||||
xhr.expect('http://ng.io/foo.html', 'xhr template');
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'});
|
||||
loader.load(view)
|
||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
|
||||
.then((el) => {
|
||||
expect(DOM.content(el)).toHaveText('xhr template');
|
||||
firstEl = el;
|
||||
return loader.load(view);
|
||||
})
|
||||
.then((el) => {
|
||||
expect(el).not.toBe(firstEl);
|
||||
expect(DOM.content(el)).toHaveText('xhr template');
|
||||
expect(el.template).toEqual('');
|
||||
expect(el.styles).toEqual(["/* foo.css */\n"]);
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should throw when no template is defined', () => {
|
||||
var view = new ViewDefinition({template: null, templateAbsUrl: null});
|
||||
expect(() => loader.load(view))
|
||||
expect(() => loader.load(new ViewDefinition({template: null, templateAbsUrl: null})))
|
||||
.toThrowError('View should have either the templateUrl or template property set');
|
||||
});
|
||||
|
||||
it('should return a rejected Promise when XHR loading fails',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/foo.html', null);
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'});
|
||||
PromiseWrapper.then(loader.load(view), function(_) { throw 'Unexpected response'; },
|
||||
function(error) {
|
||||
expect(error.message)
|
||||
.toEqual('Failed to fetch url "http://ng.io/foo.html"');
|
||||
async.done();
|
||||
});
|
||||
PromiseWrapper.then(
|
||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'})),
|
||||
function(_) { throw 'Unexpected response'; },
|
||||
function(error) {
|
||||
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.html"');
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should replace $baseUrl in attributes with the template base url',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/path/foo.html', '<img src="$baseUrl/logo.png">');
|
||||
var view = new ViewDefinition({templateAbsUrl: 'http://ng.io/path/foo.html'});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el)).toEqual('<img src="http://ng.io/path/logo.png">');
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/path/foo.html'}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('<img src="http://ng.io/path/logo.png">');
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('css', () => {
|
||||
it('should load inline styles', inject([AsyncTestCompleter], (async) => {
|
||||
var view = new ViewDefinition({template: 'html', styles: ['style 1', 'style 2']});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual('<style>style 1</style><style>style 2</style>html');
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({template: 'html', styles: ['style 1', 'style 2']}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('html');
|
||||
expect(el.styles).toEqual(['style 1', 'style 2']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should resolve urls in inline styles', inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/foo.html', 'html');
|
||||
var view = new ViewDefinition({
|
||||
templateAbsUrl: 'http://ng.io/foo.html',
|
||||
styles: ['.foo { background-image: url("double.jpg"); }']
|
||||
});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual(
|
||||
"<style>.foo { background-image: url('http://ng.io/double.jpg'); }</style>html");
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({
|
||||
templateAbsUrl: 'http://ng.io/foo.html',
|
||||
styles: ['.foo { background-image: url("double.jpg"); }']
|
||||
}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('html');
|
||||
expect(el.styles)
|
||||
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
|
@ -159,16 +141,16 @@ export function main() {
|
|||
xhr.expect('http://ng.io/foo.html', 'xhr template');
|
||||
xhr.expect('http://ng.io/foo-1.css', '1');
|
||||
xhr.expect('http://ng.io/foo-2.css', '2');
|
||||
var view = new ViewDefinition({
|
||||
templateAbsUrl: 'http://ng.io/foo.html',
|
||||
styles: ['i1'],
|
||||
styleAbsUrls: ['http://ng.io/foo-1.css', 'http://ng.io/foo-2.css']
|
||||
});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el))
|
||||
.toEqual('<style>i1</style><style>1</style><style>2</style>xhr template');
|
||||
async.done();
|
||||
});
|
||||
loader.load(new ViewDefinition({
|
||||
templateAbsUrl: 'http://ng.io/foo.html',
|
||||
styles: ['i1'],
|
||||
styleAbsUrls: ['http://ng.io/foo-1.css', 'http://ng.io/foo-2.css']
|
||||
}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual('xhr template');
|
||||
expect(el.styles).toEqual(['i1', '1', '2']);
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
|
@ -180,25 +162,27 @@ export function main() {
|
|||
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
|
||||
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
|
||||
|
||||
var view = new ViewDefinition(
|
||||
{templateAbsUrl: 'http://ng.io/foo.html', styles: ['@import "foo.css";']});
|
||||
loader.load(view).then((el) => {
|
||||
expect(DOM.getInnerHTML(el)).toEqual("<style>/* foo.css */\n</style><p>template</p>");
|
||||
async.done();
|
||||
});
|
||||
loader.load(
|
||||
new ViewDefinition(
|
||||
{templateAbsUrl: 'http://ng.io/foo.html', styles: ['@import "foo.css";']}))
|
||||
.then((el) => {
|
||||
expect(el.template).toEqual("<p>template</p>");
|
||||
expect(el.styles).toEqual(["/* foo.css */\n"]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should return a rejected Promise when XHR loading fails',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.expect('http://ng.io/foo.css', null);
|
||||
var view = new ViewDefinition({template: '', styleAbsUrls: ['http://ng.io/foo.css']});
|
||||
PromiseWrapper.then(loader.load(view), function(_) { throw 'Unexpected response'; },
|
||||
function(error) {
|
||||
expect(error.message)
|
||||
.toEqual('Failed to fetch url "http://ng.io/foo.css"');
|
||||
async.done();
|
||||
});
|
||||
PromiseWrapper.then(
|
||||
loader.load(
|
||||
new ViewDefinition({template: '', styleAbsUrls: ['http://ng.io/foo.css']})),
|
||||
function(_) { throw 'Unexpected response'; },
|
||||
function(error) {
|
||||
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.css"');
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -12,7 +12,8 @@ import {MapWrapper} from 'angular2/src/facade/collection';
|
|||
|
||||
import {ViewSplitter} from 'angular2/src/render/dom/compiler/view_splitter';
|
||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||
import {ProtoViewDto, ViewType} from 'angular2/src/render/api';
|
||||
import {CompileElement} from 'angular2/src/render/dom/compiler/compile_element';
|
||||
import {ProtoViewDto, ViewType, ViewDefinition} from 'angular2/src/render/api';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {Lexer, Parser} from 'angular2/src/change_detection/change_detection';
|
||||
|
@ -20,15 +21,23 @@ import {Lexer, Parser} from 'angular2/src/change_detection/change_detection';
|
|||
export function main() {
|
||||
describe('ViewSplitter', () => {
|
||||
|
||||
function createViewDefinition(): ViewDefinition {
|
||||
return new ViewDefinition({componentId: 'someComponent'});
|
||||
}
|
||||
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer()))]);
|
||||
}
|
||||
|
||||
function proceess(el): CompileElement[] {
|
||||
return createPipeline().processElements(el, ViewType.COMPONENT, createViewDefinition());
|
||||
}
|
||||
|
||||
describe('<template> elements', () => {
|
||||
|
||||
it('should move the content into a new <template> element and mark that as viewRoot', () => {
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
|
||||
expect(stringifyElement(results[1].element))
|
||||
.toEqual('<template class="ng-binding" if="true"></template>');
|
||||
|
@ -39,32 +48,32 @@ export function main() {
|
|||
|
||||
it('should mark the new <template> element as viewRoot', () => {
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should not wrap the root element', () => {
|
||||
var rootElement = DOM.createTemplate('');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results.length).toBe(1);
|
||||
expect(stringifyElement(rootElement)).toEqual('<template></template>');
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||
|
@ -80,7 +89,7 @@ export function main() {
|
|||
it('should replace the element with an empty <template> element', () => {
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<template><template class="ng-binding"></template></template>');
|
||||
|
@ -92,7 +101,7 @@ export function main() {
|
|||
it('should work with top-level template node', () => {
|
||||
var rootElement = DOM.createTemplate('<div template>x</div>');
|
||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(results[0].isViewRoot).toBe(true);
|
||||
|
@ -104,13 +113,13 @@ export function main() {
|
|||
|
||||
it('should mark the element as viewRoot', () => {
|
||||
var rootElement = DOM.createTemplate('<div template></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should add property bindings from the template attribute', () => {
|
||||
var rootElement = DOM.createTemplate('<div template="some-prop:expr"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[1].inheritedElementBinder.propertyBindings.get('someProp').source)
|
||||
.toEqual('expr');
|
||||
expect(results[1].attrs().get('some-prop')).toEqual('expr');
|
||||
|
@ -118,14 +127,14 @@ export function main() {
|
|||
|
||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
||||
var rootElement = DOM.createTemplate('<div template="var var-name=mapName"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedProtoView.variableBindings)
|
||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
||||
});
|
||||
|
||||
it('should add entries without value as attributes to the element', () => {
|
||||
var rootElement = DOM.createTemplate('<div template="varname"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[1].attrs().get('varname')).toEqual('');
|
||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
||||
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
|
||||
|
@ -133,26 +142,26 @@ export function main() {
|
|||
|
||||
it('should iterate properly after a template dom modification', () => {
|
||||
var rootElement = DOM.createTemplate('<div template></div><after></after>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
// 1 root + 2 initial + 2 generated template elements
|
||||
expect(results.length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||
|
@ -167,7 +176,7 @@ export function main() {
|
|||
it('should replace the element with an empty <template> element', () => {
|
||||
var rootElement = DOM.createTemplate('<span *ng-if></span>');
|
||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<template><template class="ng-binding" ng-if=""></template></template>');
|
||||
|
@ -178,14 +187,14 @@ export function main() {
|
|||
|
||||
it('should mark the element as viewRoot', () => {
|
||||
var rootElement = DOM.createTemplate('<div *foo="bar"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with top-level template node', () => {
|
||||
var rootElement = DOM.createTemplate('<div *foo>x</div>');
|
||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(results[0].isViewRoot).toBe(true);
|
||||
|
@ -197,7 +206,7 @@ export function main() {
|
|||
|
||||
it('should add property bindings from the template attribute', () => {
|
||||
var rootElement = DOM.createTemplate('<div *prop="expr"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[1].inheritedElementBinder.propertyBindings.get('prop').source)
|
||||
.toEqual('expr');
|
||||
expect(results[1].attrs().get('prop')).toEqual('expr');
|
||||
|
@ -205,14 +214,14 @@ export function main() {
|
|||
|
||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
||||
var rootElement = DOM.createTemplate('<div *foreach="var varName=mapName"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedProtoView.variableBindings)
|
||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
||||
});
|
||||
|
||||
it('should add entries without value as attribute to the element', () => {
|
||||
var rootElement = DOM.createTemplate('<div *varname></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[1].attrs().get('varname')).toEqual('');
|
||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
||||
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
|
||||
|
@ -220,26 +229,26 @@ export function main() {
|
|||
|
||||
it('should iterate properly after a template dom modification', () => {
|
||||
var rootElement = DOM.createTemplate('<div *foo></div><after></after>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
// 1 root + 2 initial + 2 generated template elements
|
||||
expect(results.length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
var results = proceess(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||
|
|
|
@ -18,9 +18,13 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||
|
||||
import {DomTestbed, TestRootView, elRef} from './dom_testbed';
|
||||
|
||||
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/render/render';
|
||||
import {
|
||||
ViewDefinition,
|
||||
DirectiveMetadata,
|
||||
RenderViewRef,
|
||||
ViewEncapsulation
|
||||
} from 'angular2/src/render/api';
|
||||
import {DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from 'angular2/src/render/dom/dom_tokens';
|
||||
import {bind} from 'angular2/di';
|
||||
|
||||
export function main() {
|
||||
|
@ -271,17 +275,17 @@ export function main() {
|
|||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
|
||||
it('should support shadow dom components',
|
||||
it('should put the template into a shadow root',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(
|
||||
someComponent,
|
||||
[
|
||||
new ViewDefinition(
|
||||
{componentId: 'someComponent', template: 'hello', directives: []})
|
||||
])
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: 'hello',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NATIVE
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('hello');
|
||||
|
@ -289,6 +293,48 @@ export function main() {
|
|||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should add styles from non native components to shadow roots while the view is not destroyed',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NATIVE,
|
||||
styles: ['a {};']
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings);
|
||||
tb.compiler.compile(new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE,
|
||||
styles: ['b {};']
|
||||
}))
|
||||
.then(_ => {
|
||||
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('a {};b {};');
|
||||
tb.renderer.destroyView(rootView.viewRef);
|
||||
tb.compiler.compile(new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE,
|
||||
styles: ['c {};']
|
||||
}))
|
||||
.then(_ => {
|
||||
expect(DOM.getShadowRoot(rootView.hostElement))
|
||||
.toHaveText('a {};b {};');
|
||||
async.done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import {MapWrapper, ListWrapper, List, Map} from 'angular2/src/facade/collection
|
|||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_tokens';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {
|
||||
RenderViewWithFragments,
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
normalizeCSS
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
EmulatedScopedShadowDomStrategy,
|
||||
} from 'angular2/src/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy';
|
||||
import {
|
||||
resetShadowDomCache,
|
||||
} from 'angular2/src/render/dom/shadow_dom/util';
|
||||
|
||||
export function main() {
|
||||
describe('EmulatedScopedShadowDomStrategy', () => {
|
||||
var styleHost, strategy;
|
||||
|
||||
beforeEach(() => {
|
||||
styleHost = el('<div></div>');
|
||||
strategy = new EmulatedScopedShadowDomStrategy(styleHost);
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should report that this is not the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(false); });
|
||||
|
||||
it('should scope styles', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
expect(styleElement).toHaveText(".foo[_ngcontent-0] {\n\n}\n\n[_nghost-0] {\n\n}");
|
||||
});
|
||||
|
||||
it('should return the same style given the same component', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
|
||||
var styleElement2 = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement2);
|
||||
|
||||
expect(DOM.getText(styleElement)).toEqual(DOM.getText(styleElement2));
|
||||
});
|
||||
|
||||
it('should return different styles given different components', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent1', 'http://base', styleElement);
|
||||
|
||||
var styleElement2 = el('<style>.foo {} :host {}</style>');
|
||||
strategy.processStyleElement('someComponent2', 'http://base', styleElement2);
|
||||
|
||||
expect(DOM.getText(styleElement)).not.toEqual(DOM.getText(styleElement2));
|
||||
});
|
||||
|
||||
it('should move the style element to the style host', () => {
|
||||
var compileElement = el('<div><style>.one {}</style></div>');
|
||||
var styleElement = DOM.firstChild(compileElement);
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
|
||||
expect(compileElement).toHaveText('');
|
||||
expect(styleHost).toHaveText('.one[_ngcontent-0] {\n\n}');
|
||||
});
|
||||
|
||||
it('should add an attribute to component elements', () => {
|
||||
var element = el('<div></div>');
|
||||
strategy.processElement(null, 'elComponent', element);
|
||||
expect(DOM.getAttribute(element, '_nghost-0')).toEqual('');
|
||||
});
|
||||
|
||||
it('should add an attribute to the content elements', () => {
|
||||
var element = el('<div></div>');
|
||||
strategy.processElement('hostComponent', null, element);
|
||||
expect(DOM.getAttribute(element, '_ngcontent-0')).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
EmulatedUnscopedShadowDomStrategy,
|
||||
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {
|
||||
resetShadowDomCache,
|
||||
} from 'angular2/src/render/dom/shadow_dom/util';
|
||||
|
||||
export function main() {
|
||||
var strategy;
|
||||
|
||||
describe('EmulatedUnscopedShadowDomStrategy', () => {
|
||||
var styleHost;
|
||||
|
||||
beforeEach(() => {
|
||||
styleHost = el('<div></div>');
|
||||
strategy = new EmulatedUnscopedShadowDomStrategy(styleHost);
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should report that this is not the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(false); });
|
||||
|
||||
it('should move the style element to the style host', () => {
|
||||
var compileElement = el('<div><style>.one {}</style></div>');
|
||||
var styleElement = DOM.firstChild(compileElement);
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleElement);
|
||||
|
||||
expect(compileElement).toHaveText('');
|
||||
expect(styleHost).toHaveText('.one {}');
|
||||
});
|
||||
|
||||
it('should insert the same style only once in the style host', () => {
|
||||
var styleEls = [
|
||||
el('<style>/*css1*/</style>'),
|
||||
el('<style>/*css2*/</style>'),
|
||||
el('<style>/*css1*/</style>')
|
||||
];
|
||||
ListWrapper.forEach(styleEls, (styleEl) => {
|
||||
strategy.processStyleElement('someComponent', 'http://base', styleEl);
|
||||
});
|
||||
|
||||
expect(styleHost).toHaveText("/*css1*//*css2*/");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {
|
||||
NativeShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
|
||||
export function main() {
|
||||
var strategy: NativeShadowDomStrategy;
|
||||
|
||||
describe('NativeShadowDomStrategy', () => {
|
||||
beforeEach(() => { strategy = new NativeShadowDomStrategy(); });
|
||||
|
||||
it('should report that this is the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(true); });
|
||||
});
|
||||
}
|
|
@ -13,7 +13,7 @@ import {
|
|||
|
||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ASTWithSource, AST} from 'angular2/src/change_detection/change_detection';
|
||||
import {PropertyBindingType, ViewType} from 'angular2/src/render/api';
|
||||
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/render/api';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
|
@ -21,8 +21,10 @@ export function main() {
|
|||
|
||||
describe('ProtoViewBuilder', () => {
|
||||
var builder;
|
||||
beforeEach(
|
||||
() => { builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED); });
|
||||
beforeEach(() => {
|
||||
builder =
|
||||
new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED, ViewEncapsulation.NONE);
|
||||
});
|
||||
|
||||
if (!IS_DARTIUM) {
|
||||
describe('verification of properties', () => {
|
||||
|
|
|
@ -21,15 +21,15 @@ import {DomTestbed} from '../dom_testbed';
|
|||
import {
|
||||
ViewDefinition,
|
||||
DirectiveMetadata,
|
||||
RenderProtoViewMergeMapping
|
||||
RenderProtoViewMergeMapping,
|
||||
ViewEncapsulation,
|
||||
ViewType
|
||||
} from 'angular2/src/render/api';
|
||||
import {bind} from 'angular2/di';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {cloneAndQueryProtoView} from 'angular2/src/render/dom/util';
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/render/render';
|
||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewMerger integration test', () => {
|
||||
|
@ -232,29 +232,46 @@ export function main() {
|
|||
});
|
||||
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
|
||||
it('should keep the non projected light dom and wrap the component view into a shadow-root element',
|
||||
runAndAssert('root', ['<a>b</a>', 'c'], [
|
||||
'<root class="ng-binding" idx="0"><shadow-root><a class="ng-binding" idx="1"><shadow-root>c</shadow-root>b</a></shadow-root></root>'
|
||||
runAndAssert('native-root', ['<a>b</a>', 'c'], [
|
||||
'<native-root class="ng-binding" idx="0"><shadow-root><a class="ng-binding" idx="1"><shadow-root>c</shadow-root>b</a></shadow-root></native-root>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('host attributes', () => {
|
||||
it('should set host attributes while merging',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compiler.compileHost(rootDirective('root'))
|
||||
.then((rootProtoViewDto) => {
|
||||
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
|
||||
ViewEncapsulation.NONE);
|
||||
builder.setHostAttribute('a', 'b');
|
||||
var componentProtoViewDto = builder.build();
|
||||
tb.merge([rootProtoViewDto, componentProtoViewDto])
|
||||
.then(mergeMappings => {
|
||||
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
|
||||
expect(DOM.getInnerHTML(domPv.rootElement))
|
||||
.toEqual('<root class="ng-binding" a="b"></root>');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
||||
expectedFragments: string[]) {
|
||||
var rootComp = DirectiveMetadata.create(
|
||||
{id: 'rootComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: hostElementName});
|
||||
var useNativeEncapsulation = hostElementName.startsWith('native-');
|
||||
var rootComp = rootDirective(hostElementName);
|
||||
return inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(rootComp, componentTemplates.map(template => new ViewDefinition({
|
||||
componentId: 'someComp',
|
||||
template: template,
|
||||
directives: [aComp, bComp, cComp]
|
||||
})))
|
||||
tb.compileAndMerge(rootComp, componentTemplates.map(template => componentView(
|
||||
template, useNativeEncapsulation ?
|
||||
ViewEncapsulation.NATIVE :
|
||||
ViewEncapsulation.NONE)))
|
||||
.then((mergeMappings) => {
|
||||
expect(stringify(mergeMappings)).toEqual(expectedFragments);
|
||||
async.done();
|
||||
|
@ -262,6 +279,21 @@ function runAndAssert(hostElementName: string, componentTemplates: string[],
|
|||
});
|
||||
}
|
||||
|
||||
function rootDirective(hostElementName: string) {
|
||||
return DirectiveMetadata.create(
|
||||
{id: 'rootComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: hostElementName});
|
||||
}
|
||||
|
||||
function componentView(template: string,
|
||||
encapsulation: ViewEncapsulation = ViewEncapsulation.NONE) {
|
||||
return new ViewDefinition({
|
||||
componentId: 'someComp',
|
||||
template: template,
|
||||
directives: [aComp, bComp, cComp],
|
||||
encapsulation: encapsulation
|
||||
});
|
||||
}
|
||||
|
||||
function stringify(protoViewMergeMapping: RenderProtoViewMergeMapping): string[] {
|
||||
var testView = cloneAndQueryProtoView(
|
||||
resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {DomSharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
|
||||
|
||||
export function main() {
|
||||
describe('DomSharedStylesHost', () => {
|
||||
var doc;
|
||||
var ssh: DomSharedStylesHost;
|
||||
var someHost: Element;
|
||||
beforeEach(() => {
|
||||
doc = DOM.createHtmlDocument();
|
||||
doc.title = '';
|
||||
ssh = new DomSharedStylesHost(doc);
|
||||
someHost = DOM.createElement('div');
|
||||
});
|
||||
|
||||
it('should add existing styles to new hosts', () => {
|
||||
ssh.addStyles(['a {};']);
|
||||
ssh.addHost(someHost);
|
||||
expect(DOM.getInnerHTML(someHost)).toEqual('<style>a {};</style>');
|
||||
});
|
||||
|
||||
it('should add new styles to hosts', () => {
|
||||
ssh.addHost(someHost);
|
||||
ssh.addStyles(['a {};']);
|
||||
expect(DOM.getInnerHTML(someHost)).toEqual('<style>a {};</style>');
|
||||
});
|
||||
|
||||
it('should add styles only once to hosts', () => {
|
||||
ssh.addStyles(['a {};']);
|
||||
ssh.addHost(someHost);
|
||||
ssh.addStyles(['a {};']);
|
||||
expect(DOM.getInnerHTML(someHost)).toEqual('<style>a {};</style>');
|
||||
});
|
||||
|
||||
it('should use the document head as default host', () => {
|
||||
ssh.addStyles(['a {};', 'b {};']);
|
||||
expect(doc.head).toHaveText('a {};b {};');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -30,7 +30,7 @@ export function main() {
|
|||
binders = [];
|
||||
}
|
||||
var rootEl = DOM.createTemplate('<div></div>');
|
||||
return DomProtoView.create(null, <Element>rootEl, [1], [], binders);
|
||||
return DomProtoView.create(null, <Element>rootEl, null, [1], [], binders, null);
|
||||
}
|
||||
|
||||
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
||||
|
|
|
@ -15,7 +15,7 @@ import {bootstrap} from 'angular2/bootstrap';
|
|||
import {Component, Directive, View} from 'angular2/annotations';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {bind} from 'angular2/di';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/render';
|
||||
|
||||
import {
|
||||
routerInjectables,
|
||||
|
|
|
@ -18,7 +18,7 @@ import {bootstrap} from 'angular2/src/core/application';
|
|||
import {Component, Directive, View} from 'angular2/src/core/annotations/decorators';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {bind} from 'angular2/di';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/render';
|
||||
import {RouteConfig, Route, Redirect} from 'angular2/src/router/route_config_decorator';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
|
|
|
@ -104,12 +104,13 @@ export function main() {
|
|||
var compiler: WorkerCompiler = createWorkerCompiler(workerSerializer, uiSerializer, tb);
|
||||
|
||||
var dirMetadata = DirectiveMetadata.create(
|
||||
{id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
{id: 'id', selector: 'custom', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
compiler.compileHost(dirMetadata)
|
||||
.then((protoView) => {
|
||||
expect(DOM.tagName(DOM.firstChild(
|
||||
DOM.content(resolveWebWorkerRef(protoView.render).rootElement))))
|
||||
.toEqual('CUSTOM');
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(
|
||||
resolveWebWorkerRef(protoView.render).rootElement)))
|
||||
.toLowerCase())
|
||||
.toEqual('custom');
|
||||
expect(protoView).not.toBeNull();
|
||||
async.done();
|
||||
});
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import {Component, View, LifecycleEvent} from 'angular2/angular2';
|
||||
import {Component, View, LifecycleEvent, ViewEncapsulation} from 'angular2/angular2';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
|
||||
@Component({selector: '[md-button]:not([href])'})
|
||||
@View({templateUrl: 'package:angular2_material/src/components/button/button.html'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdButton {
|
||||
// TODO(jelbourn): Ink ripples.
|
||||
}
|
||||
|
@ -15,7 +18,10 @@ export class MdButton {
|
|||
host: {'(click)': 'onClick($event)', '[tabIndex]': 'tabIndex'},
|
||||
lifecycle: [LifecycleEvent.onChange]
|
||||
})
|
||||
@View({templateUrl: 'package:angular2_material/src/components/button/button.html'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdAnchor {
|
||||
tabIndex: number;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, View, Attribute} from 'angular2/angular2';
|
||||
import {Component, View, Attribute, ViewEncapsulation} from 'angular2/angular2';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {KEY_SPACE} from 'angular2_material/src/core/constants';
|
||||
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
||||
|
@ -17,7 +17,8 @@ import {NumberWrapper} from 'angular2/src/facade/lang';
|
|||
})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/checkbox/checkbox.html',
|
||||
directives: []
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdCheckbox {
|
||||
/** Whether this checkbox is checked. */
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
Component,
|
||||
Directive,
|
||||
View,
|
||||
ViewEncapsulation,
|
||||
Ancestor,
|
||||
ElementRef,
|
||||
DynamicComponentLoader,
|
||||
|
@ -211,7 +212,8 @@ export class MdDialogConfig {
|
|||
})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/dialog/dialog.html',
|
||||
directives: [forwardRef(() => MdDialogContent)]
|
||||
directives: [forwardRef(() => MdDialogContent)],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
class MdDialogContainer {
|
||||
// Ref to the dialog content. Used by the DynamicComponentLoader to load the dialog content.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, View, Ancestor, LifecycleEvent} from 'angular2/angular2';
|
||||
import {Component, View, ViewEncapsulation, Ancestor, LifecycleEvent} from 'angular2/angular2';
|
||||
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {StringWrapper, isPresent, isString, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
|
@ -16,7 +16,10 @@ import {Math} from 'angular2/src/facade/math';
|
|||
properties: ['cols', 'rowHeight', 'gutterSize'],
|
||||
lifecycle: [LifecycleEvent.onAllChangesDone]
|
||||
})
|
||||
@View({templateUrl: 'package:angular2_material/src/components/grid_list/grid_list.html'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/grid_list/grid_list.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdGridList {
|
||||
/** List of tiles that are being rendered. */
|
||||
tiles: List<MdGridTile>;
|
||||
|
@ -223,7 +226,10 @@ export class MdGridList {
|
|||
},
|
||||
lifecycle: [LifecycleEvent.onDestroy, LifecycleEvent.onChange]
|
||||
})
|
||||
@View({templateUrl: 'package:angular2_material/src/components/grid_list/grid_tile.html'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/grid_list/grid_tile.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdGridTile {
|
||||
gridList: MdGridList;
|
||||
_rowspan: number;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import {Component, View} from 'angular2/angular2';
|
||||
import {Component, View, ViewEncapsulation} from 'angular2/angular2';
|
||||
|
||||
@Component({selector: 'md-progress-circular'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/progress-circular/progress_circular.html'
|
||||
templateUrl: 'package:angular2_material/src/components/progress-circular/progress_circular.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdProgressCircular {
|
||||
constructor() {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, LifecycleEvent, View, Attribute} from 'angular2/angular2';
|
||||
import {Component, LifecycleEvent, View, ViewEncapsulation, Attribute} from 'angular2/angular2';
|
||||
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {Math} from 'angular2/src/facade/math';
|
||||
|
@ -16,7 +16,8 @@ import {Math} from 'angular2/src/facade/math';
|
|||
})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/progress-linear/progress_linear.html',
|
||||
directives: []
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdProgressLinear {
|
||||
/** Value for the primary bar. */
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import {Component, View, LifecycleEvent, Ancestor, Attribute, Optional} from 'angular2/angular2';
|
||||
import {
|
||||
Component,
|
||||
View,
|
||||
ViewEncapsulation,
|
||||
LifecycleEvent,
|
||||
Ancestor,
|
||||
Attribute,
|
||||
Optional
|
||||
} from 'angular2/angular2';
|
||||
|
||||
import {isPresent, StringWrapper, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
|
||||
|
@ -36,7 +44,10 @@ var _uniqueIdCounter: number = 0;
|
|||
'[attr.aria-activedescendant]': 'activedescendant'
|
||||
}
|
||||
})
|
||||
@View({templateUrl: 'package:angular2_material/src/components/radio/radio_group.html'})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/radio/radio_group.html',
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdRadioGroup {
|
||||
/** The selected value for the radio group. The value comes from the options. */
|
||||
value: any;
|
||||
|
@ -192,7 +203,8 @@ export class MdRadioGroup {
|
|||
})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/radio/radio_button.html',
|
||||
directives: []
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdRadioButton {
|
||||
/** Whether this radio is checked. */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, View, Attribute} from 'angular2/angular2';
|
||||
import {Component, View, ViewEncapsulation, Attribute} from 'angular2/angular2';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {KEY_SPACE} from 'angular2_material/src/core/constants';
|
||||
import {KeyboardEvent} from 'angular2/src/facade/browser';
|
||||
|
@ -16,8 +16,11 @@ import {NumberWrapper} from 'angular2/src/facade/lang';
|
|||
'[attr.role]': '"checkbox"'
|
||||
}
|
||||
})
|
||||
@View(
|
||||
{templateUrl: 'package:angular2_material/src/components/switcher/switch.html', directives: []})
|
||||
@View({
|
||||
templateUrl: 'package:angular2_material/src/components/switcher/switch.html',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
})
|
||||
export class MdSwitch {
|
||||
/** Whether this switch is checked. */
|
||||
checked: boolean;
|
||||
|
|
|
@ -2,9 +2,6 @@ import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
|||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DateWrapper, Type, print} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
NativeShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
|
||||
import {
|
||||
Parser,
|
||||
|
@ -17,7 +14,6 @@ import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
|||
|
||||
import * as viewModule from 'angular2/src/core/annotations_impl/view';
|
||||
import {Component, Directive, View} from 'angular2/angular2';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
|
@ -28,7 +24,7 @@ import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabil
|
|||
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
|
||||
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import * as rc from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {ViewLoader, DefaultDomCompiler, SharedStylesHost} from 'angular2/src/render/render';
|
||||
|
||||
export function main() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
|
@ -40,12 +36,12 @@ export function main() {
|
|||
var viewResolver = new MultipleViewResolver(
|
||||
count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]);
|
||||
var urlResolver = new UrlResolver();
|
||||
var shadowDomStrategy = new NativeShadowDomStrategy();
|
||||
var renderCompiler = new rc.DefaultDomCompiler(new Parser(new Lexer()), shadowDomStrategy,
|
||||
new ViewLoader(null, null, null));
|
||||
var compiler = new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
||||
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()),
|
||||
new AppRootUrl(""));
|
||||
var appRootUrl = new AppRootUrl("");
|
||||
var renderCompiler = new DefaultDomCompiler(
|
||||
new Parser(new Lexer()), new ViewLoader(null, null, null), new SharedStylesHost(), 'a');
|
||||
var compiler =
|
||||
new Compiler(reader, cache, viewResolver, new ComponentUrlMapper(), urlResolver,
|
||||
renderCompiler, new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl);
|
||||
|
||||
function measureWrapper(func, desc) {
|
||||
return function() {
|
||||
|
|
|
@ -67,7 +67,7 @@ class DemoApp {
|
|||
<p>There are {{numCoconuts}} coconuts.</p>
|
||||
<p>Return: <input (input)="updateValue($event)"></p>
|
||||
<button type="button" (click)="done()">Done</button>
|
||||
`,
|
||||
`
|
||||
})
|
||||
class SimpleDialogComponent {
|
||||
numCoconuts: number;
|
||||
|
|
Loading…
Reference in New Issue