2016-06-23 12:47:54 -04:00
/ * *
* @license
* Copyright Google Inc . All Rights Reserved .
*
* Use of this source code is governed by an MIT - style license that can be
* found in the LICENSE file at https : //angular.io/license
* /
2016-08-19 15:51:01 -04:00
import { CommonModule } from '@angular/common' ;
2018-11-15 16:16:39 -05:00
import { Compiler , ComponentFactory , ComponentRef , ErrorHandler , EventEmitter , Host , Inject , Injectable , InjectionToken , Injector , NO_ERRORS_SCHEMA , NgModule , NgModuleRef , OnDestroy , SkipSelf , ViewRef , ɵ ivyEnabled as ivyEnabled } from '@angular/core' ;
2016-08-02 18:53:34 -04:00
import { ChangeDetectionStrategy , ChangeDetectorRef , PipeTransform } from '@angular/core/src/change_detection/change_detection' ;
2017-01-27 16:19:00 -05:00
import { getDebugContext } from '@angular/core/src/errors' ;
2016-08-16 19:48:32 -04:00
import { ComponentFactoryResolver } from '@angular/core/src/linker/component_factory_resolver' ;
2016-04-28 20:50:03 -04:00
import { ElementRef } from '@angular/core/src/linker/element_ref' ;
2016-08-02 18:53:34 -04:00
import { QueryList } from '@angular/core/src/linker/query_list' ;
2017-02-28 02:08:19 -05:00
import { TemplateRef } from '@angular/core/src/linker/template_ref' ;
2016-08-02 18:53:34 -04:00
import { ViewContainerRef } from '@angular/core/src/linker/view_container_ref' ;
import { EmbeddedViewRef } from '@angular/core/src/linker/view_ref' ;
2016-08-16 16:59:06 -04:00
import { Attribute , Component , ContentChildren , Directive , HostBinding , HostListener , Input , Output , Pipe } from '@angular/core/src/metadata' ;
2016-09-27 20:12:25 -04:00
import { TestBed , async , fakeAsync , getTestBed , tick } from '@angular/core/testing' ;
2016-08-02 18:53:34 -04:00
import { getDOM } from '@angular/platform-browser/src/dom/dom_adapter' ;
2017-02-14 19:14:40 -05:00
import { DOCUMENT } from '@angular/platform-browser/src/dom/dom_tokens' ;
2017-03-02 15:12:46 -05:00
import { dispatchEvent , el } from '@angular/platform-browser/testing/src/browser_util' ;
import { expect } from '@angular/platform-browser/testing/src/matchers' ;
2018-12-21 04:44:49 -05:00
import { fixmeIvy , modifiedInIvy , obsoleteInIvy } from '@angular/private/testing' ;
2017-03-21 11:15:10 -04:00
2017-03-02 12:37:01 -05:00
import { stringify } from '../../src/util' ;
2015-05-21 19:30:07 -04:00
2017-01-03 19:54:46 -05:00
const ANCHOR_ELEMENT = new InjectionToken ( 'AnchorElement' ) ;
2015-06-03 14:02:51 -04:00
2018-11-28 06:18:44 -05:00
if ( ivyEnabled ) {
describe ( 'ivy' , ( ) = > { declareTests ( ) ; } ) ;
} else {
describe ( 'jit' , ( ) = > { declareTests ( { useJit : true } ) ; } ) ;
describe ( 'no jit' , ( ) = > { declareTests ( { useJit : false } ) ; } ) ;
2015-12-02 13:35:51 -05:00
}
2018-11-15 16:16:39 -05:00
function declareTests ( config ? : { useJit : boolean } ) {
2015-05-21 19:30:07 -04:00
describe ( 'integration tests' , function ( ) {
2018-11-15 16:16:39 -05:00
beforeEach ( ( ) = > { TestBed . configureCompiler ( { . . . config } ) ; } ) ;
2015-06-03 14:02:51 -04:00
2015-05-21 19:30:07 -04:00
describe ( 'react to record changes' , function ( ) {
2016-08-17 10:15:35 -04:00
it ( 'should consume text node changes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div>{{ctxProp}}</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'Hello World!' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should update text node with a blank string when interpolation evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div>{{null}}{{ctxProp}}</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2015-07-24 10:29:50 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( '' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-11-04 13:55:21 -04:00
it ( 'should allow both null and undefined in expressions' , ( ) = > {
const template = '<div>{{null == undefined}}|{{null === undefined}}</div>' ;
const fixture = TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( 'true|false' ) ;
} ) ;
2016-11-07 15:23:03 -05:00
it ( 'should support an arbitrary number of interpolations in an element' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
` <div>before{{'0'}}a{{'1'}}b{{'2'}}c{{'3'}}d{{'4'}}e{{'5'}}f{{'6'}}g{{'7'}}h{{'8'}}i{{'9'}}j{{'10'}}after</div> ` ;
const fixture =
TestBed . overrideComponent ( MyComp , { set : { template } } ) . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( 'before0a1b2c3d4e5f6g7h8i9j10after' ) ;
} ) ;
it ( 'should use a blank string when interpolation evaluates to null or undefined with an arbitrary number of interpolations' ,
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
` <div>0{{null}}a{{undefined}}b{{null}}c{{undefined}}d{{null}}e{{undefined}}f{{null}}g{{undefined}}h{{null}}i{{undefined}}j{{null}}1</div> ` ;
const fixture =
TestBed . overrideComponent ( MyComp , { set : { template } } ) . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( '0abcdefghij1' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should consume element binding changes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [id]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-09-13 13:05:18 -04:00
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'id' ) )
. toEqual ( 'Hello World!' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should consume binding to aria-* attributes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [attr.aria-label]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Initial aria label' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'aria-label' ) )
. toEqual ( 'Initial aria label' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Changed aria label' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'aria-label' ) )
. toEqual ( 'Changed aria label' ) ;
} ) ;
it ( 'should remove an attribute when attribute expression evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [attr.foo]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'bar' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'foo' ) )
. toEqual ( 'bar' ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . hasAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'foo' ) )
. toBeFalsy ( ) ;
} ) ;
it ( 'should remove style when when style expression evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [style.height.px]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = '10' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getStyle ( fixture . debugElement . children [ 0 ] . nativeElement , 'height' ) )
. toEqual ( '10px' ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getStyle ( fixture . debugElement . children [ 0 ] . nativeElement , 'height' ) )
. toEqual ( '' ) ;
} ) ;
2015-11-04 04:16:47 -05:00
2015-05-21 19:30:07 -04:00
it ( 'should consume binding to property names where attr name and property name do not match' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [tabindex]="ctxNumProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'tabIndex' ) )
. toEqual ( 0 ) ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxNumProp = 5 ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'tabIndex' ) )
. toEqual ( 5 ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should consume binding to camel-cased properties' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<input [readOnly]="ctxBoolProp">' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'readOnly' ) )
. toBeFalsy ( ) ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'readOnly' ) )
. toBeTruthy ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should consume binding to innerHtml' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div innerHtml="{{ctxProp}}"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Some <span>HTML</span>' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getInnerHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toEqual ( 'Some <span>HTML</span>' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Some other <div>HTML</div>' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getInnerHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toEqual ( 'Some other <div>HTML</div>' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-03 20:57:07 -05:00
modifiedInIvy ( 'Binding to the class property directly works differently' )
. it ( 'should consume binding to className using class alias' , ( ) = > {
2018-11-30 11:14:53 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div class="initial" [class]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:14:53 -05:00
const nativeEl = fixture . debugElement . children [ 0 ] . nativeElement ;
fixture . componentInstance . ctxProp = 'foo bar' ;
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:14:53 -05:00
expect ( nativeEl ) . toHaveCssClass ( 'foo' ) ;
expect ( nativeEl ) . toHaveCssClass ( 'bar' ) ;
expect ( nativeEl ) . not . toHaveCssClass ( 'initial' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-11-28 06:18:44 -05:00
it ( 'should consume binding to htmlFor using for alias' , ( ) = > {
const template = '<label [for]="ctxProp"></label>' ;
const fixture = TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
2016-11-09 18:21:27 -05:00
2018-11-28 06:18:44 -05:00
const nativeEl = fixture . debugElement . children [ 0 ] . nativeElement ;
fixture . debugElement . componentInstance . ctxProp = 'foo' ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getProperty ( nativeEl , 'htmlFor' ) ) . toBe ( 'foo' ) ;
} ) ;
2016-11-09 18:21:27 -05:00
2018-11-30 11:45:04 -05:00
it ( 'should consume directive watch expression change.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<span>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'<div my-dir elprop="Hi there!"></div>' +
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
'</span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:45:04 -05:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
fixture . detectChanges ( ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:45:04 -05:00
const containerSpan = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:45:04 -05:00
expect ( containerSpan . children [ 0 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hello World!' ) ;
expect ( containerSpan . children [ 1 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hi there!' ) ;
expect ( containerSpan . children [ 2 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hi there!' ) ;
expect ( containerSpan . children [ 3 ] . injector . get ( MyDir ) . dirProp )
. toEqual ( 'One more Hello World!' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
describe ( 'pipes' , ( ) = > {
2018-11-30 11:45:04 -05:00
it ( 'should support pipes in bindings' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir , DoublePipe ] } ) ;
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:45:04 -05:00
fixture . componentInstance . ctxProp = 'a' ;
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:45:04 -05:00
const dir = fixture . debugElement . children [ 0 ] . references ! [ 'dir' ] ;
expect ( dir . dirProp ) . toEqual ( 'aa' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support nested components.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<child-cmp></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
// GH issue 328 - https://github.com/angular/angular/issues/328
2018-11-30 11:45:04 -05:00
it ( 'should support different directive types on a single node' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp , MyDir ] } ) ;
const template = '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:45:04 -05:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:45:04 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2015-05-21 19:30:07 -04:00
2018-11-30 11:45:04 -05:00
expect ( tc . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hello World!' ) ;
expect ( tc . injector . get ( ChildComp ) . dirProp ) . toEqual ( null ) ;
} ) ;
2015-08-12 04:44:46 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a binding attribute is not given' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<p my-dir></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should execute a given directive once, even if specified multiple times' , ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DuplicateDir , DuplicateDir , [ DuplicateDir , [ DuplicateDir ] ] ] } ) ;
const template = '<p no-duplicate></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'noduplicate' ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a selector matches property binding' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , IdDir ] } ) ;
const template = '<p [id]="ctxProp"></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const idDir = tc . injector . get ( IdDir ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'some_id' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( idDir . id ) . toEqual ( 'some_id' ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'other_id' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( idDir . id ) . toEqual ( 'other_id' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a selector matches event binding' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , EventDir ] } ) ;
const template = '<p (customEvent)="doNothing()"></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-06-23 17:02:01 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2018-07-05 08:24:53 -04:00
expect ( tc . injector . get ( EventDir ) ) . not . toBeNull ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-680: Throw meaningful error for uninitialized @Output' )
. it ( 'should display correct error message for uninitialized @Output' , ( ) = > {
2018-11-20 11:00:41 -05:00
@Component ( { selector : 'my-uninitialized-output' , template : '<p>It works!</p>' } )
class UninitializedOutputComp {
@Output ( ) customEvent ! : EventEmitter < any > ;
}
2017-09-08 19:41:13 -04:00
2018-11-20 11:00:41 -05:00
const template =
'<my-uninitialized-output (customEvent)="doNothing()"></my-uninitialized-output>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
2018-01-16 12:30:28 -05:00
2018-11-20 11:00:41 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , UninitializedOutputComp ] } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError (
'@Output customEvent not initialized in \'UninitializedOutputComp\'.' ) ;
} ) ;
2017-09-08 19:41:13 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should read directives metadata from their binding token' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , PrivateImpl , NeedsPublicApi ] } ) ;
const template = '<div public-api><div needs-public-api></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-03 20:57:07 -05:00
modifiedInIvy ( 'Comment node order changed' )
. it ( 'should support template directives via `<ng-template>` elements.' , ( ) = > {
2018-11-20 11:00:41 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeViewport ] } ) ;
const template =
'<ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-11-20 11:00:41 -05:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2018-11-20 11:00:41 -05:00
const childNodesOfWrapper = getDOM ( ) . childNodes ( fixture . nativeElement ) ;
// 1 template + 2 copies.
expect ( childNodesOfWrapper . length ) . toBe ( 3 ) ;
expect ( childNodesOfWrapper [ 1 ] ) . toHaveText ( 'hello' ) ;
expect ( childNodesOfWrapper [ 2 ] ) . toHaveText ( 'again' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should not share empty context for template directives - issue #10045' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , PollutedContext , NoContext ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<ng-template pollutedContext let-foo="bar">{{foo}}</ng-template><ng-template noContext let-foo="bar">{{foo}}</ng-template>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'baz' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-06-16 14:16:08 -04:00
2018-12-17 08:19:25 -05:00
it ( 'should not detach views in ViewContainers when the parent view is destroyed.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeViewport ] } ) ;
const template =
'<div *ngIf="ctxBoolProp"><ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-12-17 08:19:25 -05:00
fixture . componentInstance . ctxBoolProp = true ;
fixture . detectChanges ( ) ;
2016-02-03 14:35:42 -05:00
2018-12-17 08:19:25 -05:00
const ngIfEl = fixture . debugElement . children [ 0 ] ;
const someViewport : SomeViewport =
ngIfEl . childNodes
2018-12-19 16:06:43 -05:00
. find (
debugElement = > debugElement . nativeNode . nodeType ===
Node . COMMENT_NODE ) ! . injector . get ( SomeViewport ) ;
2018-12-17 08:19:25 -05:00
expect ( someViewport . container . length ) . toBe ( 2 ) ;
expect ( ngIfEl . children . length ) . toBe ( 2 ) ;
2015-05-21 19:30:07 -04:00
2018-12-17 08:19:25 -05:00
fixture . componentInstance . ctxBoolProp = false ;
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2018-12-17 08:19:25 -05:00
expect ( someViewport . container . length ) . toBe ( 2 ) ;
expect ( fixture . debugElement . children . length ) . toBe ( 0 ) ;
} ) ;
2016-08-08 12:11:35 -04:00
2017-01-09 16:16:46 -05:00
it ( 'should use a comment while stamping out `<ng-template>` elements.' , ( ) = > {
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template : '<ng-template></ng-template>' } } )
. createComponent ( MyComp ) ;
2016-05-04 14:53:12 -04:00
2016-11-12 08:08:58 -05:00
const childNodesOfWrapper = getDOM ( ) . childNodes ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
expect ( childNodesOfWrapper . length ) . toBe ( 1 ) ;
expect ( getDOM ( ) . isCommentNode ( childNodesOfWrapper [ 0 ] ) ) . toBe ( true ) ;
} ) ;
2015-11-20 16:46:37 -05:00
2016-08-17 10:15:35 -04:00
it ( 'should allow to transplant TemplateRefs into other ViewContainers' , ( ) = > {
2016-08-19 15:51:01 -04:00
TestBed . configureTestingModule ( {
declarations : [
MyComp , SomeDirective , CompWithHost , ToolbarComponent , ToolbarViewContainer , ToolbarPart
] ,
2016-08-23 13:52:40 -04:00
imports : [ CommonModule ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-19 15:51:01 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
const template =
2017-01-09 16:16:46 -05:00
'<some-directive><toolbar><ng-template toolbarpart let-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></ng-template></toolbar></some-directive>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'From myComp' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement )
2016-08-17 10:15:35 -04:00
. toHaveText ( 'TOOLBAR(From myComp,From toolbar,Component with an injected host)' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
describe ( 'reference bindings' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should assign a component to a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<p><child-cmp ref-alice></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'alice' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ChildComp ) ;
} ) ;
it ( 'should assign a directive to a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ExportDir ] } ) ;
const template = '<div><div export-dir #localdir="dir"></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'localdir' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ExportDir ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-708: Directives with multiple exports are not supported' )
. it ( 'should assign a directive to a ref when it has multiple exportAs names' , ( ) = > {
2018-11-20 11:00:41 -05:00
TestBed . configureTestingModule (
{ declarations : [ MyComp , DirectiveWithMultipleExportAsNames ] } ) ;
2017-08-15 19:34:47 -04:00
2018-11-20 11:00:41 -05:00
const template = '<div multiple-export-as #x="dirX" #y="dirY"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
2017-08-15 19:34:47 -04:00
2018-11-20 11:00:41 -05:00
const fixture = TestBed . createComponent ( MyComp ) ;
expect ( fixture . debugElement . children [ 0 ] . references ! [ 'x' ] )
. toBeAnInstanceOf ( DirectiveWithMultipleExportAsNames ) ;
expect ( fixture . debugElement . children [ 0 ] . references ! [ 'y' ] )
. toBeAnInstanceOf ( DirectiveWithMultipleExportAsNames ) ;
} ) ;
2017-08-15 19:34:47 -04:00
2016-01-06 17:13:44 -05:00
it ( 'should make the assigned component accessible in property bindings, even if they were declared before the component' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<ng-template [ngIf]="true">{{alice.ctxProp}}</ng-template>|{{alice.ctxProp}}|<child-cmp ref-alice></child-cmp>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello|hello|hello' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should assign two component instances each with a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template =
'<p><child-cmp ref-alice></child-cmp><child-cmp ref-bob></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const pEl = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2017-03-29 12:34:45 -04:00
const alice = pEl . children [ 0 ] . references ! [ 'alice' ] ;
const bob = pEl . children [ 1 ] . references ! [ 'bob' ] ;
2016-08-17 10:15:35 -04:00
expect ( alice ) . toBeAnInstanceOf ( ChildComp ) ;
expect ( bob ) . toBeAnInstanceOf ( ChildComp ) ;
expect ( alice ) . not . toBe ( bob ) ;
} ) ;
it ( 'should assign the component instance to a ref- with shorthand syntax' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<child-cmp #alice></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . references ! [ 'alice' ] )
. toBeAnInstanceOf ( ChildComp ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should assign the element instance to a user-defined variable' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div><div ref-alice><i>Hello</i></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const value = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'alice' ] ;
2016-08-17 10:15:35 -04:00
expect ( value ) . not . toBe ( null ) ;
expect ( value . tagName . toLowerCase ( ) ) . toEqual ( 'div' ) ;
} ) ;
2018-12-19 17:35:56 -05:00
fixmeIvy (
'FW-870: DebugNode.references gets comment node instead of TemplateRef for template nodes' )
2018-12-03 20:57:07 -05:00
. it ( 'should assign the TemplateRef to a user-defined variable' , ( ) = > {
2018-11-20 11:00:41 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent (
MyComp , { set : { template : '<ng-template ref-alice></ng-template>' } } )
. createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-20 11:00:41 -05:00
const value = fixture . debugElement . childNodes [ 0 ] . references ! [ 'alice' ] ;
expect ( value . createEmbeddedView ) . toBeTruthy ( ) ;
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should preserve case' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<p><child-cmp ref-superAlice></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'superAlice' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ChildComp ) ;
} ) ;
2016-04-25 22:52:24 -04:00
} ) ;
2015-06-15 21:05:29 -04:00
2016-04-25 22:52:24 -04:00
describe ( 'variables' , ( ) = > {
2018-12-03 20:57:07 -05:00
modifiedInIvy ( 'Comment node order changed' )
. it ( 'should allow to use variables in a for loop' , ( ) = > {
2018-11-22 09:23:41 -05:00
const template =
'<ng-template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</ng-template>' ;
2017-01-09 16:16:46 -05:00
2018-11-22 09:23:41 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildCompNoTemplate ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-22 09:23:41 -05:00
fixture . detectChanges ( ) ;
// Get the element at index 2, since index 0 is the <ng-template>.
expect ( getDOM ( ) . childNodes ( fixture . nativeElement ) [ 2 ] ) . toHaveText ( '1-hello' ) ;
} ) ;
2015-06-04 16:45:08 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
describe ( 'OnPush components' , ( ) = > {
2016-01-06 17:13:44 -05:00
2018-12-20 03:49:24 -05:00
it ( 'should use ChangeDetectorRef to manually request a check' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithRef ] ] ] } ) ;
const template = '<push-cmp-with-ref #cmp></push-cmp-with-ref>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
cmp . propagate ( ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
it ( 'should be checked when its bindings got updated' , ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmp , EventCmp ] , imports : [ CommonModule ] } ) ;
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
fixture . componentInstance . ctxProp = 'one' ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2018-12-20 03:49:24 -05:00
fixture . componentInstance . ctxProp = 'two' ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2018-12-05 09:52:35 -05:00
it ( 'should allow to destroy a component from within a host event handler' ,
fakeAsync ( ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithHostEvent ] ] ] } ) ;
const template = '<push-cmp-with-host-event></push-cmp-with-host-event>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
tick ( ) ;
fixture . detectChanges ( ) ;
const cmpEl = fixture . debugElement . children [ 0 ] ;
const cmp : PushCmpWithHostEvent = cmpEl . injector . get ( PushCmpWithHostEvent ) ;
cmp . ctxCallback = ( _ : any ) = > fixture . destroy ( ) ;
expect ( ( ) = > cmpEl . triggerEventHandler ( 'click' , < Event > { } ) ) . not . toThrow ( ) ;
} ) ) ;
2016-02-17 15:12:10 -05:00
}
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-758: OnPush events not marking view dirty when using renderer2' )
. it ( 'should be checked when an event is fired' , ( ) = > {
2018-11-30 11:14:53 -05:00
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmp , EventCmp ] , imports : [ CommonModule ] } ) ;
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:14:53 -05:00
const cmpEl = fixture . debugElement . children [ 0 ] ;
const cmp = cmpEl . componentInstance ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:14:53 -05:00
// regular element
cmpEl . children [ 0 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:14:53 -05:00
// element inside of an *ngIf
cmpEl . children [ 1 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 3 ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:14:53 -05:00
// element inside a nested component
cmpEl . children [ 2 ] . children [ 0 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 4 ) ;
2017-03-21 13:44:11 -04:00
2018-11-30 11:14:53 -05:00
// host element
cmpEl . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 5 ) ;
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should not affect updating properties on the component' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithRef ] ] ] } ) ;
const template = '<push-cmp-with-ref [prop]="ctxProp" #cmp></push-cmp-with-ref>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'one' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . prop ) . toEqual ( 'one' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'two' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . prop ) . toEqual ( 'two' ) ;
} ) ;
2015-08-06 13:39:02 -04:00
2018-12-20 03:49:24 -05:00
it ( 'should be checked when an async pipe requests a check' , fakeAsync ( ( ) = > {
2018-12-20 12:16:09 -05:00
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmpWithAsyncPipe ] , imports : [ CommonModule ] } ) ;
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2018-12-20 03:49:24 -05:00
2018-12-20 12:16:09 -05:00
tick ( ) ;
2018-12-20 03:49:24 -05:00
2018-12-20 12:16:09 -05:00
const cmp : PushCmpWithAsyncPipe = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2018-12-20 03:49:24 -05:00
2018-12-20 12:16:09 -05:00
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2018-12-20 03:49:24 -05:00
2018-12-20 12:16:09 -05:00
cmp . resolve ( 2 ) ;
tick ( ) ;
2018-12-20 03:49:24 -05:00
2018-12-20 12:16:09 -05:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should create a component that injects an @Host' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeDirective , CompWithHost ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-05-21 19:30:07 -04:00
< some - directive >
< p >
2015-07-29 14:26:09 -04:00
< cmp - with - host # child > < / c m p - w i t h - h o s t >
2015-05-21 19:30:07 -04:00
< / p >
2016-08-17 10:15:35 -04:00
< / s o m e - d i r e c t i v e > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const childComponent =
2017-03-29 12:34:45 -04:00
fixture . debugElement . children [ 0 ] . children [ 0 ] . children [ 0 ] . references ! [ 'child' ] ;
2016-08-17 10:15:35 -04:00
expect ( childComponent . myHost ) . toBeAnInstanceOf ( SomeDirective ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should create a component that injects an @Host through viewcontainer directive' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeDirective , CompWithHost ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-05-21 19:30:07 -04:00
< some - directive >
2015-11-23 19:02:19 -05:00
< p * ngIf = "true" >
2015-07-29 14:26:09 -04:00
< cmp - with - host # child > < / c m p - w i t h - h o s t >
2015-05-21 19:30:07 -04:00
< / p >
2016-08-17 10:15:35 -04:00
< / s o m e - d i r e c t i v e > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-06-23 17:02:01 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] . children [ 0 ] ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const childComponent = tc . references ! [ 'child' ] ;
2016-08-17 10:15:35 -04:00
expect ( childComponent . myHost ) . toBeAnInstanceOf ( SomeDirective ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-14 09:11:14 -05:00
it ( 'should support events via EventEmitter on regular elements' , async ( ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] } ) ;
const template = '<div emitter listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] ;
const emitter = tc . injector . get ( DirectiveEmittingEvent ) ;
const listener = tc . injector . get ( DirectiveListeningEvent ) ;
expect ( listener . msg ) . toEqual ( '' ) ;
let eventCount = 0 ;
emitter . event . subscribe ( {
next : ( ) = > {
eventCount ++ ;
if ( eventCount === 1 ) {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
fixture . destroy ( ) ;
emitter . fireEvent ( 'fired again !' ) ;
} else {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
}
}
} ) ;
emitter . fireEvent ( 'fired !' ) ;
} ) ) ;
2015-05-21 19:30:07 -04:00
2018-12-17 08:19:25 -05:00
it ( 'should support events via EventEmitter on template elements' , async ( ( ) = > {
const fixture =
TestBed
. configureTestingModule (
{ declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] } )
. overrideComponent ( MyComp , {
set : {
template :
'<ng-template emitter listener (event)="ctxProp=$event"></ng-template>'
}
} )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . childNodes . find (
2018-12-19 16:06:43 -05:00
debugElement = > debugElement . nativeNode . nodeType === Node . COMMENT_NODE ) ! ;
2018-12-17 08:19:25 -05:00
const emitter = tc . injector . get ( DirectiveEmittingEvent ) ;
const myComp = fixture . debugElement . injector . get ( MyComp ) ;
const listener = tc . injector . get ( DirectiveListeningEvent ) ;
myComp . ctxProp = '' ;
expect ( listener . msg ) . toEqual ( '' ) ;
emitter . event . subscribe ( {
next : ( ) = > {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
expect ( myComp . ctxProp ) . toEqual ( 'fired !' ) ;
}
} ) ;
emitter . fireEvent ( 'fired !' ) ;
} ) ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
it ( 'should support [()] syntax' , async ( ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTwoWayBinding ] } ) ;
const template = '<div [(control)]="ctxProp" two-way></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const dir = tc . injector . get ( DirectiveWithTwoWayBinding ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'one' ;
2016-08-19 15:51:01 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
expect ( dir . control ) . toEqual ( 'one' ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
dir . controlChange . subscribe (
{ next : ( ) = > { expect ( fixture . componentInstance . ctxProp ) . toEqual ( 'two' ) ; } } ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
dir . triggerChange ( 'two' ) ;
} ) ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
it ( 'should support render events' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveListeningDomEvent ] } ) ;
const template = '<div listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2018-12-19 18:03:47 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
dispatchEvent ( tc . nativeElement , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
expect ( listener . eventTypes ) . toEqual ( [
'domEvent' , 'body_domEvent' , 'document_domEvent' , 'window_domEvent'
] ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
fixture . destroy ( ) ;
listener . eventTypes = [ ] ;
dispatchEvent ( tc . nativeElement , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ ] ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
it ( 'should support render global events' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveListeningDomEvent ] } ) ;
const template = '<div listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
const doc = TestBed . get ( DOCUMENT ) ;
2016-08-17 10:15:35 -04:00
2018-12-19 18:03:47 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ 'window_domEvent' ] ) ;
listener . eventTypes = [ ] ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'document' ) , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ 'document_domEvent' , 'window_domEvent' ] ) ;
fixture . destroy ( ) ;
listener . eventTypes = [ ] ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'body' ) , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ ] ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2016-11-08 18:46:55 -05:00
it ( 'should support updating host element via hostAttributes on root elements' , ( ) = > {
@Component ( { host : { 'role' : 'button' } , template : '' } )
class ComponentUpdatingHostAttributes {
}
TestBed . configureTestingModule ( { declarations : [ ComponentUpdatingHostAttributes ] } ) ;
const fixture = TestBed . createComponent ( ComponentUpdatingHostAttributes ) ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . nativeElement , 'role' ) ) . toEqual ( 'button' ) ;
} ) ;
it ( 'should support updating host element via hostAttributes on host elements' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveUpdatingHostAttributes ] } ) ;
const template = '<div update-host-attributes></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'role' ) )
. toEqual ( 'button' ) ;
} ) ;
2018-11-22 09:23:41 -05:00
it ( 'should support updating host element via hostProperties' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveUpdatingHostProperties ] } ) ;
const template = '<div update-host-properties></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const updateHost = tc . injector . get ( DirectiveUpdatingHostProperties ) ;
2016-08-17 10:15:35 -04:00
updateHost . id = 'newId' ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( tc . nativeElement , 'id' ) ) . toEqual ( 'newId' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-681: not possible to retrieve host property bindings from TView' )
. it ( 'should not use template variables for expressions in hostProperties' , ( ) = > {
2018-11-22 00:14:06 -05:00
@Directive (
{ selector : '[host-properties]' , host : { '[id]' : 'id' , '[title]' : 'unknownProp' } } )
class DirectiveWithHostProps {
id = 'one' ;
}
2017-02-02 18:01:35 -05:00
2018-11-22 00:14:06 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostProps ] } )
. overrideComponent ( MyComp , {
set : { template : ` <div *ngFor="let id of ['forId']" host-properties></div> ` }
} )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-02-02 18:01:35 -05:00
2018-11-22 00:14:06 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
expect ( tc . properties [ 'id' ] ) . toBe ( 'one' ) ;
expect ( tc . properties [ 'title' ] ) . toBe ( undefined ) ;
} ) ;
2016-10-20 18:24:58 -04:00
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-725: Pipes in host bindings fail with a cryptic error' )
. it ( 'should not allow pipes in hostProperties' , ( ) = > {
2018-11-22 09:23:41 -05:00
@Directive ( { selector : '[host-properties]' , host : { '[id]' : 'id | uppercase' } } )
class DirectiveWithHostProps {
}
2016-10-20 18:24:58 -04:00
2018-11-22 09:23:41 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostProps ] } ) ;
const template = '<div host-properties></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( /Host binding expression cannot contain pipes/ ) ;
} ) ;
2016-10-20 18:24:58 -04:00
2017-02-09 17:59:57 -05:00
it ( 'should not use template variables for expressions in hostListeners' , ( ) = > {
2016-10-20 18:24:58 -04:00
@Directive ( { selector : '[host-listener]' , host : { '(click)' : 'doIt(id, unknownProp)' } } )
class DirectiveWithHostListener {
id = 'one' ;
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
receivedArgs ! : any [ ] ;
2016-10-20 18:24:58 -04:00
doIt ( . . . args : any [ ] ) { this . receivedArgs = args ; }
}
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostListener ] } )
. overrideComponent (
MyComp ,
{ set : { template : ` <div *ngFor="let id of ['forId']" host-listener></div> ` } } )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2016-10-20 18:24:58 -04:00
tc . triggerEventHandler ( 'click' , { } ) ;
2016-11-12 08:08:58 -05:00
const dir : DirectiveWithHostListener = tc . injector . get ( DirectiveWithHostListener ) ;
2016-10-20 18:24:58 -04:00
expect ( dir . receivedArgs ) . toEqual ( [ 'one' , undefined ] ) ;
} ) ;
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-742: Pipes in host listeners should throw a descriptive error' )
. it ( 'should not allow pipes in hostListeners' , ( ) = > {
2018-11-27 08:38:01 -05:00
@Directive ( { selector : '[host-listener]' , host : { '(click)' : 'doIt() | somePipe' } } )
class DirectiveWithHostListener {
}
2016-10-20 18:24:58 -04:00
2018-11-27 08:38:01 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostListener ] } ) ;
const template = '<div host-listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( /Cannot have a pipe in an action expression/ ) ;
} ) ;
2016-10-20 18:24:58 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2016-08-17 10:15:35 -04:00
it ( 'should support preventing default on render events' , ( ) = > {
TestBed . configureTestingModule ( {
declarations :
[ MyComp , DirectiveListeningDomEventPrevent , DirectiveListeningDomEventNoPrevent ]
} ) ;
const template =
'<input type="checkbox" listenerprevent><input type="checkbox" listenernoprevent>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const dispatchedEvent = getDOM ( ) . createMouseEvent ( 'click' ) ;
const dispatchedEvent2 = getDOM ( ) . createMouseEvent ( 'click' ) ;
2016-08-17 10:15:35 -04:00
getDOM ( ) . dispatchEvent ( fixture . debugElement . children [ 0 ] . nativeElement , dispatchedEvent ) ;
getDOM ( ) . dispatchEvent ( fixture . debugElement . children [ 1 ] . nativeElement , dispatchedEvent2 ) ;
expect ( getDOM ( ) . isPrevented ( dispatchedEvent ) ) . toBe ( true ) ;
expect ( getDOM ( ) . isPrevented ( dispatchedEvent2 ) ) . toBe ( false ) ;
expect ( getDOM ( ) . getChecked ( fixture . debugElement . children [ 0 ] . nativeElement ) ) . toBeFalsy ( ) ;
expect ( getDOM ( ) . getChecked ( fixture . debugElement . children [ 1 ] . nativeElement ) ) . toBeTruthy ( ) ;
} ) ;
2015-05-21 19:30:07 -04:00
}
2018-12-19 18:03:47 -05:00
it ( 'should support render global events from multiple directives' , ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DirectiveListeningDomEvent , DirectiveListeningDomEventOther ] } ) ;
const template = '<div *ngIf="ctxBoolProp" listener listenerother></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
const doc = TestBed . get ( DOCUMENT ) ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
globalCounter = 0 ;
fixture . componentInstance . ctxBoolProp = true ;
fixture . detectChanges ( ) ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
const listenerother = tc . injector . get ( DirectiveListeningDomEventOther ) ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ 'window_domEvent' ] ) ;
expect ( listenerother . eventType ) . toEqual ( 'other_domEvent' ) ;
expect ( globalCounter ) . toEqual ( 1 ) ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
fixture . componentInstance . ctxBoolProp = false ;
fixture . detectChanges ( ) ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
expect ( globalCounter ) . toEqual ( 1 ) ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
fixture . componentInstance . ctxBoolProp = true ;
fixture . detectChanges ( ) ;
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
expect ( globalCounter ) . toEqual ( 2 ) ;
2018-11-20 11:00:41 -05:00
2018-12-19 18:03:47 -05:00
// need to destroy to release all remaining global event listeners
fixture . destroy ( ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2017-08-09 17:11:51 -04:00
describe ( 'ViewContainerRef' , ( ) = > {
2016-08-16 19:48:32 -04:00
beforeEach ( ( ) = > {
// we need a module to declarate ChildCompUsingService as an entryComponent otherwise the
// factory doesn't get created
@NgModule ( {
2016-08-17 10:15:35 -04:00
declarations : [ MyComp , DynamicViewport , ChildCompUsingService ] ,
2016-08-23 13:52:40 -04:00
entryComponents : [ ChildCompUsingService ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-16 19:48:32 -04:00
} )
class MyModule {
}
TestBed . configureTestingModule ( { imports : [ MyModule ] } ) ;
TestBed . overrideComponent (
MyComp , { add : { template : '<div><dynamic-vp #dynamic></dynamic-vp></div>' } } ) ;
} ) ;
2017-08-09 17:11:51 -04:00
describe ( '.createComponent' , ( ) = > {
it ( 'should allow to create a component at any bound location' , async ( ( ) = > {
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 1 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
} ) ) ;
2017-03-21 11:15:10 -04:00
2017-08-09 17:11:51 -04:00
it ( 'should allow to create multiple components at a location' , async ( ( ) = > {
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
dynamicVp . create ( ) ;
dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 1 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 2 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
} ) ) ;
2017-03-21 11:15:10 -04:00
2019-01-08 23:16:17 -05:00
fixmeIvy ( 'FW-929: ModuleWithComponentFactories.componentFactories is never filled in' )
2018-12-03 20:57:07 -05:00
. it ( 'should create a component that has been freshly compiled' , ( ) = > {
2018-11-20 11:00:41 -05:00
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
@NgModule ( {
declarations : [ RootComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
@NgModule ( {
declarations : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someValue' } ] ,
} )
class MyModule {
}
const compFixture = TestBed . configureTestingModule ( { imports : [ RootModule ] } )
. createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myCompFactory =
< ComponentFactory < MyComp > > compiler . compileModuleAndAllComponentsSync ( MyModule )
. componentFactories [ 0 ] ;
// Note: the ComponentFactory was created directly via the compiler, i.e. it
// does not have an association to an NgModuleRef.
// -> expect the providers of the module that the view container belongs to.
const compRef = compFixture . componentInstance . vc . createComponent ( myCompFactory ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someRootValue' ) ;
} ) ;
2019-01-07 11:29:24 -05:00
it ( 'should create a component with the passed NgModuleRef' , ( ) = > {
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
2018-11-20 11:00:41 -05:00
2019-01-07 11:29:24 -05:00
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
2018-11-20 11:00:41 -05:00
2019-01-07 11:29:24 -05:00
@NgModule ( {
declarations : [ RootComp , MyComp ] ,
entryComponents : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
2018-11-20 11:00:41 -05:00
2019-01-07 11:29:24 -05:00
@NgModule ( { providers : [ { provide : 'someToken' , useValue : 'someValue' } ] } )
class MyModule {
}
2018-11-20 11:00:41 -05:00
2019-01-07 11:29:24 -05:00
const compFixture =
TestBed . configureTestingModule ( { imports : [ RootModule ] } ) . createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myModule = compiler . compileModuleSync ( MyModule ) . create ( TestBed . get ( NgModuleRef ) ) ;
const myCompFactory = ( < ComponentFactoryResolver > TestBed . get ( ComponentFactoryResolver ) )
. resolveComponentFactory ( MyComp ) ;
// Note: MyComp was declared as entryComponent in the RootModule,
// but we pass MyModule to the createComponent call.
// -> expect the providers of MyModule!
const compRef = compFixture . componentInstance . vc . createComponent (
myCompFactory , undefined , undefined , undefined , myModule ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someValue' ) ;
} ) ;
2018-11-20 11:00:41 -05:00
2019-01-07 11:29:24 -05:00
it ( 'should create a component with the NgModuleRef of the ComponentFactoryResolver' ,
( ) = > {
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
@NgModule ( {
declarations : [ RootComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
@NgModule ( {
declarations : [ MyComp ] ,
entryComponents : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someValue' } ] ,
} )
class MyModule {
}
const compFixture = TestBed . configureTestingModule ( { imports : [ RootModule ] } )
. createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myModule =
compiler . compileModuleSync ( MyModule ) . create ( TestBed . get ( NgModuleRef ) ) ;
const myCompFactory =
myModule . componentFactoryResolver . resolveComponentFactory ( MyComp ) ;
// Note: MyComp was declared as entryComponent in MyModule,
// and we don't pass an explicit ModuleRef to the createComponent call.
// -> expect the providers of MyModule!
const compRef = compFixture . componentInstance . vc . createComponent ( myCompFactory ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someValue' ) ;
} ) ;
2017-03-21 11:15:10 -04:00
} ) ;
2017-08-09 17:11:51 -04:00
describe ( '.insert' , ( ) = > {
2018-11-27 08:38:01 -05:00
it ( 'should throw with destroyed views' , async ( ( ) = > {
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
const ref = dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
2018-11-20 11:00:41 -05:00
2018-11-27 08:38:01 -05:00
ref . destroy ( ) ;
expect ( ( ) = > {
dynamicVp . insert ( ref . hostView ) ;
} ) . toThrowError ( 'Cannot insert a destroyed View in a ViewContainer!' ) ;
} ) ) ;
2017-08-09 17:11:51 -04:00
} ) ;
2017-03-21 11:15:10 -04:00
2017-08-09 17:11:51 -04:00
describe ( '.move' , ( ) = > {
2018-11-27 08:38:01 -05:00
it ( 'should throw with destroyed views' , async ( ( ) = > {
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
const ref = dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
2018-11-20 11:00:41 -05:00
2018-11-27 08:38:01 -05:00
ref . destroy ( ) ;
expect ( ( ) = > {
dynamicVp . move ( ref . hostView , 1 ) ;
} ) . toThrowError ( 'Cannot move a destroyed View in a ViewContainer!' ) ;
} ) ) ;
2017-03-21 11:15:10 -04:00
} ) ;
2017-08-09 17:11:51 -04:00
2015-05-21 19:30:07 -04:00
} ) ;
2018-11-22 10:03:26 -05:00
it ( 'should support static attributes' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , NeedsAttribute ] } ) ;
const template = '<input static type="text" title>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const needsAttribute = tc . injector . get ( NeedsAttribute ) ;
2016-08-17 10:15:35 -04:00
expect ( needsAttribute . typeAttribute ) . toEqual ( 'text' ) ;
expect ( needsAttribute . staticAttribute ) . toEqual ( '' ) ;
2018-07-05 08:24:53 -04:00
expect ( needsAttribute . fooAttribute ) . toBeNull ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2016-06-20 12:52:41 -04:00
2018-11-29 19:21:16 -05:00
it ( 'should support custom interpolation' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [
MyComp , ComponentWithCustomInterpolationA , ComponentWithCustomInterpolationB ,
ComponentWithDefaultInterpolation
]
} ) ;
const template = ` <div>{{ctxProp}}</div>
2016-06-20 12:52:41 -04:00
< cmp - with - custom - interpolation - a > < / c m p - w i t h - c u s t o m - i n t e r p o l a t i o n - a >
2016-08-17 10:15:35 -04:00
< cmp - with - custom - interpolation - b > < / c m p - w i t h - c u s t o m - i n t e r p o l a t i o n - b > ` ;
2018-11-29 19:21:16 -05:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-29 19:21:16 -05:00
fixture . componentInstance . ctxProp = 'Default Interpolation' ;
2016-08-17 10:15:35 -04:00
2018-11-29 19:21:16 -05:00
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement )
. toHaveText (
'Default InterpolationCustom Interpolation ACustom Interpolation B (Default Interpolation)' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
describe ( 'dependency injection' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support bindings' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveProvidingInjectable , DirectiveConsumingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-07-09 14:40:09 -04:00
< directive - providing - injectable >
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable # consuming >
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
< / d i r e c t i v e - p r o v i d i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const comp = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'consuming' ] ;
2016-08-17 10:15:35 -04:00
expect ( comp . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2018-12-13 05:14:33 -05:00
it ( 'should support viewProviders' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveProvidingInjectableInView , DirectiveConsumingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
const template = `
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable # consuming >
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
2018-12-13 05:14:33 -05:00
TestBed . overrideComponent ( DirectiveProvidingInjectableInView , { set : { template } } ) ;
const fixture = TestBed . createComponent ( DirectiveProvidingInjectableInView ) ;
2015-05-21 19:30:07 -04:00
2018-12-13 05:14:33 -05:00
const comp = fixture . debugElement . children [ 0 ] . references ! [ 'consuming' ] ;
expect ( comp . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support unbounded lookup' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [
MyComp , DirectiveProvidingInjectable , DirectiveContainingDirectiveConsumingAnInjectable ,
DirectiveConsumingInjectableUnbounded
2016-08-23 13:52:40 -04:00
] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-05-21 19:30:07 -04:00
< directive - providing - injectable >
< directive - containing - directive - consuming - an - injectable # dir >
< / d i r e c t i v e - c o n t a i n i n g - d i r e c t i v e - c o n s u m i n g - a n - i n j e c t a b l e >
< / d i r e c t i v e - p r o v i d i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
TestBed . overrideComponent ( DirectiveContainingDirectiveConsumingAnInjectable , {
set : {
template : `
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable - unbounded > < / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e - u n b o u n d e d >
2016-08-17 10:15:35 -04:00
`
}
} ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const comp = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'dir' ] ;
2016-08-17 10:15:35 -04:00
expect ( comp . directive . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
2015-05-21 19:56:02 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support the event-bus scenario' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [
MyComp , GrandParentProvidingEventBus , ParentProvidingEventBus , ChildConsumingEventBus
2016-08-23 13:52:40 -04:00
] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-05-21 19:56:02 -04:00
< grand - parent - providing - event - bus >
< parent - providing - event - bus >
< child - consuming - event - bus >
< / c h i l d - c o n s u m i n g - e v e n t - b u s >
< / p a r e n t - p r o v i d i n g - e v e n t - b u s >
< / g r a n d - p a r e n t - p r o v i d i n g - e v e n t - b u s >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const gpComp = fixture . debugElement . children [ 0 ] ;
const parentComp = gpComp . children [ 0 ] ;
const childComp = parentComp . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2016-11-12 08:08:58 -05:00
const grandParent = gpComp . injector . get ( GrandParentProvidingEventBus ) ;
const parent = parentComp . injector . get ( ParentProvidingEventBus ) ;
const child = childComp . injector . get ( ChildConsumingEventBus ) ;
2016-08-17 10:15:35 -04:00
expect ( grandParent . bus . name ) . toEqual ( 'grandparent' ) ;
expect ( parent . bus . name ) . toEqual ( 'parent' ) ;
expect ( parent . grandParentBus ) . toBe ( grandParent . bus ) ;
expect ( child . bus ) . toBe ( parent . bus ) ;
} ) ;
2016-06-08 19:38:52 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should instantiate bindings lazily' , ( ) = > {
TestBed . configureTestingModule ( {
2016-08-23 13:52:40 -04:00
declarations : [ MyComp , DirectiveConsumingInjectable , ComponentProvidingLoggingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-07-13 19:28:44 -04:00
< component - providing - logging - injectable # providing >
2015-11-23 19:02:19 -05:00
< directive - consuming - injectable * ngIf = "ctxBoolProp" >
2015-07-13 19:28:44 -04:00
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
< / c o m p o n e n t - p r o v i d i n g - l o g g i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const providing = fixture . debugElement . children [ 0 ] . references ! [ 'providing' ] ;
2016-08-17 10:15:35 -04:00
expect ( providing . created ) . toBe ( false ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( providing . created ) . toBe ( true ) ;
} ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
describe ( 'corner cases' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should remove script tags from templates' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = `
2015-08-05 10:35:59 -04:00
< script > alert ( "Ooops" ) ; < / script >
2016-08-17 10:15:35 -04:00
< div > before < script > alert ( "Ooops" ) ; < / script > < span > inside < / span > after < / div > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-07-28 13:39:10 -04:00
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . querySelectorAll ( fixture . nativeElement , 'script' ) . length ) . toEqual ( 0 ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2016-07-28 13:39:10 -04:00
2019-01-09 11:55:23 -05:00
it ( 'should throw when using directives without selector' , ( ) = > {
@Directive ( { } )
class SomeDirective {
}
2016-07-28 13:39:10 -04:00
2019-01-09 11:55:23 -05:00
@Component ( { selector : 'comp' , template : '' } )
class SomeComponent {
}
2016-08-17 10:15:35 -04:00
2019-01-09 11:55:23 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeDirective , SomeComponent ] } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( ` Directive ${ stringify ( SomeDirective ) } has no selector, please add it! ` ) ;
} ) ;
2016-07-28 13:39:10 -04:00
2018-11-28 06:18:44 -05:00
it ( 'should use a default element name for components without selectors' , ( ) = > {
let noSelectorComponentFactory : ComponentFactory < SomeComponent > = undefined ! ;
2016-07-28 13:39:10 -04:00
2018-11-28 06:18:44 -05:00
@Component ( { template : '----' } )
class NoSelectorComponent {
}
2016-08-19 15:51:01 -04:00
2018-11-28 06:18:44 -05:00
@Component ( { selector : 'some-comp' , template : '' , entryComponents : [ NoSelectorComponent ] } )
class SomeComponent {
constructor ( componentFactoryResolver : ComponentFactoryResolver ) {
// grab its own component factory
noSelectorComponentFactory =
componentFactoryResolver . resolveComponentFactory ( NoSelectorComponent ) ! ;
}
}
2016-08-19 15:51:01 -04:00
2018-11-28 06:18:44 -05:00
TestBed . configureTestingModule ( { declarations : [ SomeComponent , NoSelectorComponent ] } ) ;
2016-08-19 15:51:01 -04:00
2018-11-28 06:18:44 -05:00
// get the factory
TestBed . createComponent ( SomeComponent ) ;
2016-08-19 15:51:01 -04:00
2018-11-28 06:18:44 -05:00
expect ( noSelectorComponentFactory . selector ) . toBe ( 'ng-component' ) ;
2017-03-14 19:26:17 -04:00
2018-11-28 06:18:44 -05:00
expect (
getDOM ( )
. nodeName ( noSelectorComponentFactory . create ( Injector . NULL ) . location . nativeElement )
. toLowerCase ( ) )
. toEqual ( 'ng-component' ) ;
} ) ;
2015-08-05 10:35:59 -04:00
} ) ;
2017-02-28 02:08:19 -05:00
describe ( 'error handling' , ( ) = > {
2018-12-11 13:43:02 -05:00
it ( 'should report a meaningful error when a directive is missing annotation' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeDirectiveMissingAnnotation ] } ) ;
2018-11-20 11:00:41 -05:00
2018-12-11 13:43:02 -05:00
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError (
` Unexpected value ' ${ stringify ( SomeDirectiveMissingAnnotation ) } ' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation. ` ) ;
} ) ;
2015-05-26 12:45:15 -04:00
2018-12-11 13:43:02 -05:00
it ( 'should report a meaningful error when a component is missing view annotation' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ComponentWithoutView ] } ) ;
try {
TestBed . createComponent ( ComponentWithoutView ) ;
} catch ( e ) {
expect ( e . message ) . toContain (
` No template specified for component ${ stringify ( ComponentWithoutView ) } ` ) ;
}
} ) ;
2016-08-17 10:15:35 -04:00
2018-12-21 04:44:49 -05:00
obsoleteInIvy ( 'DebugContext is not patched on exceptions in ivy' )
2018-12-03 20:57:07 -05:00
. it ( 'should provide an error context when an error happens in DI' , ( ) = > {
2018-11-20 11:00:41 -05:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveThrowingAnError ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
const template = ` <directive-throwing-error></directive-throwing-error> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
try {
TestBed . createComponent ( MyComp ) ;
throw 'Should throw' ;
} catch ( e ) {
const c = getDebugContext ( e ) ;
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
}
} ) ;
2015-07-22 15:00:35 -04:00
2018-12-21 04:44:49 -05:00
obsoleteInIvy ( 'DebugContext is not patched on exceptions in ivy' )
2018-12-03 20:57:07 -05:00
. it ( 'should provide an error context when an error happens in change detection' , ( ) = > {
2018-11-20 11:00:41 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveThrowingAnError ] } ) ;
const template = ` <input [value]="one.two.three" #local> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
try {
fixture . detectChanges ( ) ;
throw 'Should throw' ;
} catch ( e ) {
const c = getDebugContext ( e ) ;
expect ( getDOM ( ) . nodeName ( c . renderNode ) . toUpperCase ( ) ) . toEqual ( 'INPUT' ) ;
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
expect ( c . context ) . toBe ( fixture . componentInstance ) ;
expect ( c . references [ 'local' ] ) . toBeDefined ( ) ;
}
} ) ;
2015-07-23 21:01:34 -04:00
2018-12-21 04:44:49 -05:00
obsoleteInIvy ( 'DebugContext is not patched on exceptions in ivy' )
2018-12-03 20:57:07 -05:00
. it ( 'should provide an error context when an error happens in change detection (text node)' ,
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = ` <div>{{one.two.three}}</div> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
try {
fixture . detectChanges ( ) ;
throw 'Should throw' ;
} catch ( e ) {
const c = getDebugContext ( e ) ;
expect ( c . renderNode ) . toBeTruthy ( ) ;
}
} ) ;
2015-07-23 21:01:34 -04:00
2018-12-21 04:44:49 -05:00
obsoleteInIvy ( 'DebugContext is not patched on exceptions in ivy' )
. it ( 'should provide an error context when an error happens in an event handler' ,
fakeAsync ( ( ) = > {
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
const template = ` <span emitter listener (event)="throwError()" #local></span> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
tick ( ) ;
const tc = fixture . debugElement . children [ 0 ] ;
const errorHandler = tc . injector . get ( ErrorHandler ) ;
let err : any ;
spyOn ( errorHandler , 'handleError' ) . and . callFake ( ( e : any ) = > err = e ) ;
tc . injector . get ( DirectiveEmittingEvent ) . fireEvent ( 'boom' ) ;
expect ( err ) . toBeTruthy ( ) ;
const c = getDebugContext ( err ) ;
expect ( getDOM ( ) . nodeName ( c . renderNode ) . toUpperCase ( ) ) . toEqual ( 'SPAN' ) ;
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
expect ( c . context ) . toBe ( fixture . componentInstance ) ;
expect ( c . references [ 'local' ] ) . toBeDefined ( ) ;
} ) ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support imperative views' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SimpleImperativeViewComponent ] } ) ;
const template = '<simple-imp-cmp></simple-imp-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello imp view' ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support moving embedded views around' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeImperativeViewport ] ,
providers : [ { provide : ANCHOR_ELEMENT , useValue : el ( '<div></div>' ) } ] ,
} ) ;
const template = '<div><div *someImpvp="ctxBoolProp">hello</div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const anchorElement = getTestBed ( ) . get ( ANCHOR_ELEMENT ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( anchorElement ) . toHaveText ( '' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( anchorElement ) . toHaveText ( 'hello' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = false ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( '' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-06-03 14:02:51 -04:00
2015-07-30 20:58:56 -04:00
describe ( 'Property bindings' , ( ) = > {
2018-12-03 20:57:07 -05:00
fixmeIvy ( 'FW-721: Bindings to unknown properties are not reported as errors' )
. it ( 'should throw on bindings to unknown properties' , ( ) = > {
2018-11-22 09:23:41 -05:00
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div unknown="{{ctxProp}}"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
try {
TestBed . createComponent ( MyComp ) ;
throw 'Should throw' ;
} catch ( e ) {
expect ( e . message ) . toMatch (
/Template parse errors:\nCan't bind to 'unknown' since it isn't a known property of 'div'. \("<div \[ERROR ->\]unknown="{{ctxProp}}"><\/div>"\): .*MyComp.html@0:5/ ) ;
}
} ) ;
2015-07-10 17:55:48 -04:00
2016-07-21 20:12:00 -04:00
it ( 'should not throw for property binding to a non-existing property when there is a matching directive property' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div my-dir [elprop]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) ) . not . toThrow ( ) ;
} ) ;
it ( 'should not be created when there is a directive with the same property' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTitle ] } ) ;
const template = '<span [title]="ctxProp"></span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'TITLE' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const el = getDOM ( ) . querySelector ( fixture . nativeElement , 'span' ) ;
2017-02-15 00:03:18 -05:00
expect ( el . title ) . toBeFalsy ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-07-30 20:58:56 -04:00
2018-12-10 17:51:28 -05:00
it ( 'should work when a directive uses hostProperty to update the DOM element' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTitleAndHostProperty ] } ) ;
const template = '<span [title]="ctxProp"></span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-07-30 20:58:56 -04:00
2018-12-10 17:51:28 -05:00
fixture . componentInstance . ctxProp = 'TITLE' ;
fixture . detectChanges ( ) ;
2016-08-17 10:15:35 -04:00
2018-12-10 17:51:28 -05:00
const el = getDOM ( ) . querySelector ( fixture . nativeElement , 'span' ) ;
expect ( getDOM ( ) . getProperty ( el , 'title' ) ) . toEqual ( 'TITLE' ) ;
} ) ;
2015-07-30 20:58:56 -04:00
} ) ;
2015-08-20 18:11:12 -04:00
describe ( 'logging property updates' , ( ) = > {
2018-11-30 11:45:04 -05:00
it ( 'should reflect property values as attributes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2018-11-20 11:00:41 -05:00
2018-11-30 11:45:04 -05:00
fixture . componentInstance . ctxProp = 'hello' ;
fixture . detectChanges ( ) ;
2018-11-20 11:00:41 -05:00
2018-11-30 11:45:04 -05:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) )
. toContain ( 'ng-reflect-dir-prop="hello"' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2018-11-28 06:18:44 -05:00
it ( ` should work with prop names containing ' $ ' ` , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ ParentCmp , SomeCmpWithInput ] } ) ;
const fixture = TestBed . createComponent ( ParentCmp ) ;
fixture . detectChanges ( ) ;
2016-08-17 10:15:35 -04:00
2018-11-28 06:18:44 -05:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) ) . toContain ( 'ng-reflect-test_="hello"' ) ;
} ) ;
2015-11-19 14:14:44 -05:00
2018-11-29 10:39:43 -05:00
it ( 'should reflect property values on template comments' , ( ) = > {
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent (
MyComp , { set : { template : '<ng-template [ngIf]="ctxBoolProp"></ng-template>' } } )
. createComponent ( MyComp ) ;
2017-02-08 06:36:43 -05:00
2018-11-29 10:39:43 -05:00
fixture . componentInstance . ctxBoolProp = true ;
fixture . detectChanges ( ) ;
2017-02-08 06:36:43 -05:00
2018-11-29 10:39:43 -05:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) )
. toContain ( '"ng\-reflect\-ng\-if"\: "true"' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:45:04 -05:00
it ( 'should indicate when toString() throws' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div my-dir [elprop]="toStringThrow"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-11-30 11:45:04 -05:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) ) . toContain ( '[ERROR]' ) ;
} ) ;
2015-08-20 18:11:12 -04:00
} ) ;
2015-06-17 10:05:35 -04:00
2015-09-03 18:10:48 -04:00
describe ( 'property decorators' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support property decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators elProp="aaa"></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-08-17 10:15:35 -04:00
expect ( dir . dirProp ) . toEqual ( 'aaa' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support host binding decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-08-17 10:15:35 -04:00
dir . myAttr = 'aaa' ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getOuterHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toContain ( 'my-attr="aaa"' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2016-08-17 10:15:35 -04:00
it ( 'should support event decorators' , fakeAsync ( ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = ` <with-prop-decorators (elEvent)="ctxProp='called'"> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-04-18 19:04:35 -04:00
tick ( ) ;
2015-09-03 18:10:48 -04:00
2016-11-12 08:08:58 -05:00
const emitter =
2016-06-14 17:53:01 -04:00
fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-04-18 19:04:35 -04:00
emitter . fireEvent ( 'fired !' ) ;
2015-09-03 18:10:48 -04:00
2016-04-18 19:04:35 -04:00
tick ( ) ;
2015-09-03 18:10:48 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . componentInstance . ctxProp ) . toEqual ( 'called' ) ;
2016-08-17 10:15:35 -04:00
} ) ) ;
it ( 'should support host listener decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
const native = fixture . debugElement . children [ 0 ] . nativeElement ;
2016-08-17 10:15:35 -04:00
getDOM ( ) . dispatchEvent ( native , getDOM ( ) . createMouseEvent ( 'click' ) ) ;
expect ( dir . target ) . toBe ( native ) ;
} ) ;
2015-09-03 18:10:48 -04:00
}
2015-10-06 20:03:37 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support defining views in the component decorator' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , ComponentWithTemplate ] ,
imports : [ CommonModule ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<component-with-template></component-with-template>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const native = fixture . debugElement . children [ 0 ] . nativeElement ;
2016-08-17 10:15:35 -04:00
expect ( native ) . toHaveText ( 'No View Decorator: 123' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
} ) ;
2015-10-27 16:16:27 -04:00
2017-07-28 09:58:28 -04:00
describe ( 'whitespaces in templates' , ( ) = > {
it ( 'should not remove whitespaces by default' , async ( ( ) = > {
@Component ( {
selector : 'comp' ,
template : '<span>foo</span> <span>bar</span>' ,
} )
class MyCmp {
}
const f = TestBed . configureTestingModule ( { declarations : [ MyCmp ] } ) . createComponent ( MyCmp ) ;
f . detectChanges ( ) ;
2018-02-05 18:37:05 -05:00
expect ( f . nativeElement . childNodes . length ) . toBe ( 2 ) ;
2017-07-28 09:58:28 -04:00
} ) ) ;
it ( 'should not remove whitespaces when explicitly requested not to do so' , async ( ( ) = > {
@Component ( {
selector : 'comp' ,
template : '<span>foo</span> <span>bar</span>' ,
preserveWhitespaces : true ,
} )
class MyCmp {
}
const f = TestBed . configureTestingModule ( { declarations : [ MyCmp ] } ) . createComponent ( MyCmp ) ;
f . detectChanges ( ) ;
expect ( f . nativeElement . childNodes . length ) . toBe ( 3 ) ;
} ) ) ;
it ( 'should remove whitespaces when explicitly requested to do so' , async ( ( ) = > {
@Component ( {
selector : 'comp' ,
template : '<span>foo</span> <span>bar</span>' ,
preserveWhitespaces : false ,
} )
class MyCmp {
}
const f = TestBed . configureTestingModule ( { declarations : [ MyCmp ] } ) . createComponent ( MyCmp ) ;
f . detectChanges ( ) ;
expect ( f . nativeElement . childNodes . length ) . toBe ( 2 ) ;
} ) ) ;
} ) ;
2015-11-19 14:14:44 -05:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2015-10-27 16:16:27 -04:00
describe ( 'svg' , ( ) = > {
2018-12-12 10:21:58 -05:00
fixmeIvy ( 'FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")' )
. it ( 'should support svg elements' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<svg><use xlink:href="Port" /></svg>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
const el = fixture . nativeElement ;
const svg = getDOM ( ) . childNodes ( el ) [ 0 ] ;
const use = getDOM ( ) . childNodes ( svg ) [ 0 ] ;
expect ( getDOM ( ) . getProperty ( < Element > svg , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > use , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
const firstAttribute = getDOM ( ) . getProperty ( < Element > use , 'attributes' ) [ 0 ] ;
expect ( firstAttribute . name ) . toEqual ( 'xlink:href' ) ;
expect ( firstAttribute . namespaceURI ) . toEqual ( 'http://www.w3.org/1999/xlink' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
fixmeIvy ( 'FW-811: Align HTML namespaces between Ivy and Render2' )
. it ( 'should support foreignObjects with document fragments' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
'<svg><foreignObject><xhtml:div><p>Test</p></xhtml:div></foreignObject></svg>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
const el = fixture . nativeElement ;
const svg = getDOM ( ) . childNodes ( el ) [ 0 ] ;
const foreignObject = getDOM ( ) . childNodes ( svg ) [ 0 ] ;
const p = getDOM ( ) . childNodes ( foreignObject ) [ 0 ] ;
expect ( getDOM ( ) . getProperty ( < Element > svg , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > foreignObject , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > p , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/1999/xhtml' ) ;
} ) ;
2015-10-27 16:16:27 -04:00
} ) ;
2016-01-08 15:01:29 -05:00
describe ( 'attributes' , ( ) = > {
2018-12-12 10:21:58 -05:00
fixmeIvy ( 'FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")' )
. it ( 'should support attributes with namespace' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeCmp ] } ) ;
const template = '<svg:use xlink:href="#id" />' ;
TestBed . overrideComponent ( SomeCmp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( SomeCmp ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
const useEl = getDOM ( ) . firstChild ( fixture . nativeElement ) ;
expect ( getDOM ( ) . getAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( '#id' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
fixmeIvy ( 'FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")' )
. it ( 'should support binding to attributes with namespace' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeCmp ] } ) ;
const template = '<svg:use [attr.xlink:href]="value" />' ;
TestBed . overrideComponent ( SomeCmp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( SomeCmp ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
const cmp = fixture . componentInstance ;
const useEl = getDOM ( ) . firstChild ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
cmp . value = '#id' ;
fixture . detectChanges ( ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
expect ( getDOM ( ) . getAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( '#id' ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
cmp . value = null ;
fixture . detectChanges ( ) ;
2016-08-17 10:15:35 -04:00
2018-12-12 10:21:58 -05:00
expect ( getDOM ( ) . hasAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( false ) ;
} ) ;
2016-01-08 15:01:29 -05:00
} ) ;
2015-10-27 16:16:27 -04:00
}
2015-05-21 19:30:07 -04:00
} ) ;
}
2016-06-20 12:52:41 -04:00
@Component ( { selector : 'cmp-with-default-interpolation' , template : ` {{text}} ` } )
class ComponentWithDefaultInterpolation {
text = 'Default Interpolation' ;
}
@Component ( {
selector : 'cmp-with-custom-interpolation-a' ,
template : ` <div>{%text%}</div> ` ,
interpolation : [ '{%' , '%}' ]
} )
class ComponentWithCustomInterpolationA {
text = 'Custom Interpolation A' ;
}
@Component ( {
selector : 'cmp-with-custom-interpolation-b' ,
template :
` <div>{**text%}</div> (<cmp-with-default-interpolation></cmp-with-default-interpolation>) ` ,
2016-08-19 15:51:01 -04:00
interpolation : [ '{**' , '%}' ]
2016-06-20 12:52:41 -04:00
} )
class ComponentWithCustomInterpolationB {
text = 'Custom Interpolation B' ;
}
2015-05-21 19:30:07 -04:00
@Injectable ( )
class MyService {
greeting : string ;
constructor ( ) { this . greeting = 'hello' ; }
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'simple-imp-cmp' , template : '' } )
2015-05-21 19:30:07 -04:00
class SimpleImperativeViewComponent {
2016-06-14 17:53:01 -04:00
done : any ;
2015-05-21 19:30:07 -04:00
2017-02-02 18:01:35 -05:00
constructor ( self : ElementRef ) {
2016-11-12 08:08:58 -05:00
const hostElement = self . nativeElement ;
2016-04-28 20:50:03 -04:00
getDOM ( ) . appendChild ( hostElement , el ( 'hello imp view' ) ) ;
2015-05-21 19:30:07 -04:00
}
}
@Directive ( { selector : 'dynamic-vp' } )
class DynamicViewport {
2016-11-01 17:21:40 -04:00
private componentFactory : ComponentFactory < ChildCompUsingService > ;
private injector : Injector ;
constructor ( private vc : ViewContainerRef , componentFactoryResolver : ComponentFactoryResolver ) {
2016-11-12 08:08:58 -05:00
const myService = new MyService ( ) ;
2015-05-21 19:30:07 -04:00
myService . greeting = 'dynamic greet' ;
2015-06-29 14:15:49 -04:00
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
this . injector = Injector . create ( [ { provide : MyService , useValue : myService } ] , vc . injector ) ;
2017-03-29 12:34:45 -04:00
this . componentFactory =
componentFactoryResolver . resolveComponentFactory ( ChildCompUsingService ) ! ;
2015-05-21 19:30:07 -04:00
}
2016-11-01 17:21:40 -04:00
2017-08-09 17:11:51 -04:00
create ( ) : ComponentRef < ChildCompUsingService > {
return this . vc . createComponent ( this . componentFactory , this . vc . length , this . injector ) ;
}
insert ( viewRef : ViewRef , index? : number ) : ViewRef { return this . vc . insert ( viewRef , index ) ; }
move ( viewRef : ViewRef , currentIndex : number ) : ViewRef {
return this . vc . move ( viewRef , currentIndex ) ;
}
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[my-dir]' , inputs : [ 'dirProp: elprop' ] , exportAs : 'mydir' } )
2015-05-21 19:30:07 -04:00
class MyDir {
dirProp : string ;
constructor ( ) { this . dirProp = '' ; }
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[title]' , inputs : [ 'title' ] } )
2015-07-30 20:58:56 -04:00
class DirectiveWithTitle {
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
title ! : string ;
2015-07-30 20:58:56 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[title]' , inputs : [ 'title' ] , host : { '[title]' : 'title' } } )
2015-07-30 20:58:56 -04:00
class DirectiveWithTitleAndHostProperty {
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
title ! : string ;
2015-07-30 20:58:56 -04:00
}
2016-04-29 15:04:03 -04:00
@Component ( { selector : 'event-cmp' , template : '<div (click)="noop()"></div>' } )
class EventCmp {
noop() { }
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'push-cmp' ,
inputs : [ 'prop' ] ,
2017-03-21 13:44:11 -04:00
host : { '(click)' : 'true' } ,
2016-03-08 16:36:48 -05:00
changeDetection : ChangeDetectionStrategy.OnPush ,
2016-04-29 15:04:03 -04:00
template :
2016-08-19 15:51:01 -04:00
'{{field}}<div (click)="noop()"></div><div *ngIf="true" (click)="noop()"></div><event-cmp></event-cmp>'
2016-03-08 16:36:48 -05:00
} )
2015-05-21 19:30:07 -04:00
class PushCmp {
numberOfChecks : number ;
2016-06-14 17:53:01 -04:00
prop : any ;
2015-05-21 19:30:07 -04:00
constructor ( ) { this . numberOfChecks = 0 ; }
2016-04-29 15:04:03 -04:00
noop() { }
2015-05-21 19:30:07 -04:00
get field() {
this . numberOfChecks ++ ;
2016-06-08 19:38:52 -04:00
return 'fixed' ;
2015-05-21 19:30:07 -04:00
}
}
2015-08-26 14:44:59 -04:00
@Component ( {
selector : 'push-cmp-with-ref' ,
2015-09-30 23:59:23 -04:00
inputs : [ 'prop' ] ,
2016-03-08 16:36:48 -05:00
changeDetection : ChangeDetectionStrategy.OnPush ,
template : '{{field}}'
2015-08-26 14:44:59 -04:00
} )
2015-05-21 19:30:07 -04:00
class PushCmpWithRef {
numberOfChecks : number ;
ref : ChangeDetectorRef ;
2016-06-14 17:53:01 -04:00
prop : any ;
2015-05-21 19:30:07 -04:00
constructor ( ref : ChangeDetectorRef ) {
this . numberOfChecks = 0 ;
this . ref = ref ;
}
get field() {
this . numberOfChecks ++ ;
2016-06-08 19:38:52 -04:00
return 'fixed' ;
2015-05-21 19:30:07 -04:00
}
2015-08-28 13:08:18 -04:00
propagate() { this . ref . markForCheck ( ) ; }
2015-05-21 19:30:07 -04:00
}
2016-02-17 15:12:10 -05:00
@Component ( {
selector : 'push-cmp-with-host-event' ,
host : { '(click)' : 'ctxCallback($event)' } ,
changeDetection : ChangeDetectionStrategy.OnPush ,
template : ''
} )
class PushCmpWithHostEvent {
2016-06-14 17:53:01 -04:00
ctxCallback : Function = ( _ : any ) = > { } ;
2016-02-17 15:12:10 -05:00
}
2015-11-02 19:03:42 -05:00
@Component ( {
selector : 'push-cmp-with-async' ,
changeDetection : ChangeDetectionStrategy.OnPush ,
2016-08-19 15:51:01 -04:00
template : '{{field | async}}'
2015-11-02 19:03:42 -05:00
} )
2015-08-06 13:39:02 -04:00
class PushCmpWithAsyncPipe {
numberOfChecks : number = 0 ;
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
resolve ! : ( result : any ) = > void ;
2015-08-06 13:39:02 -04:00
promise : Promise < any > ;
constructor ( ) {
2016-08-02 18:53:34 -04:00
this . promise = new Promise ( ( resolve ) = > { this . resolve = resolve ; } ) ;
2015-08-06 13:39:02 -04:00
}
get field() {
this . numberOfChecks ++ ;
return this . promise ;
}
}
2016-08-19 15:51:01 -04:00
@Component ( { selector : 'my-comp' , template : '' } )
2015-05-21 19:30:07 -04:00
class MyComp {
ctxProp : string ;
2015-11-19 14:14:44 -05:00
ctxNumProp : number ;
ctxBoolProp : boolean ;
2016-07-12 13:26:54 -04:00
toStringThrow = { toString : function ( ) { throw 'boom' ; } } ;
2015-05-21 19:30:07 -04:00
constructor ( ) {
this . ctxProp = 'initial value' ;
this . ctxNumProp = 0 ;
this . ctxBoolProp = false ;
}
2015-07-27 18:47:42 -04:00
throwError() { throw 'boom' ; }
2015-05-21 19:30:07 -04:00
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'child-cmp' ,
inputs : [ 'dirProp' ] ,
viewProviders : [ MyService ] ,
template : '{{ctxProp}}'
} )
2015-05-21 19:30:07 -04:00
class ChildComp {
ctxProp : string ;
2017-03-29 12:34:45 -04:00
dirProp : string | null ;
2015-05-21 19:30:07 -04:00
constructor ( service : MyService ) {
this . ctxProp = service . greeting ;
this . dirProp = null ;
}
}
2016-08-19 15:51:01 -04:00
@Component ( { selector : 'child-cmp-no-template' , template : '' } )
2015-06-15 21:05:29 -04:00
class ChildCompNoTemplate {
ctxProp : string = 'hello' ;
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'child-cmp-svc' , template : '{{ctxProp}}' } )
2015-05-21 19:30:07 -04:00
class ChildCompUsingService {
ctxProp : string ;
constructor ( service : MyService ) { this . ctxProp = service . greeting ; }
}
@Directive ( { selector : 'some-directive' } )
class SomeDirective {
}
class SomeDirectiveMissingAnnotation { }
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'cmp-with-host' ,
template : '<p>Component with an injected host</p>' ,
} )
2015-07-29 14:26:09 -04:00
class CompWithHost {
myHost : SomeDirective ;
constructor ( @Host ( ) someComp : SomeDirective ) { this . myHost = someComp ; }
2015-05-21 19:30:07 -04:00
}
2015-10-11 01:11:13 -04:00
@Component ( { selector : '[child-cmp2]' , viewProviders : [ MyService ] } )
2015-05-21 19:30:07 -04:00
class ChildComp2 {
ctxProp : string ;
2017-03-29 12:34:45 -04:00
dirProp : string | null ;
2015-05-21 19:30:07 -04:00
constructor ( service : MyService ) {
this . ctxProp = service . greeting ;
this . dirProp = null ;
}
}
2016-04-28 17:00:31 -04:00
class SomeViewportContext {
constructor ( public someTmpl : string ) { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[some-viewport]' } )
class SomeViewport {
2016-05-04 14:53:12 -04:00
constructor ( public container : ViewContainerRef , templateRef : TemplateRef < SomeViewportContext > ) {
2016-04-28 17:00:31 -04:00
container . createEmbeddedView ( templateRef , new SomeViewportContext ( 'hello' ) ) ;
container . createEmbeddedView ( templateRef , new SomeViewportContext ( 'again' ) ) ;
2015-05-21 19:30:07 -04:00
}
}
2016-08-08 12:11:35 -04:00
@Directive ( { selector : '[pollutedContext]' } )
class PollutedContext {
constructor ( private tplRef : TemplateRef < any > , private vcRef : ViewContainerRef ) {
const evRef = this . vcRef . createEmbeddedView ( this . tplRef ) ;
evRef . context . bar = 'baz' ;
}
}
@Directive ( { selector : '[noContext]' } )
class NoContext {
constructor ( private tplRef : TemplateRef < any > , private vcRef : ViewContainerRef ) {
this . vcRef . createEmbeddedView ( this . tplRef ) ;
}
}
2015-08-07 14:41:38 -04:00
@Pipe ( { name : 'double' } )
2015-11-17 13:09:23 -05:00
class DoublePipe implements PipeTransform , OnDestroy {
ngOnDestroy() { }
2016-06-14 17:53:01 -04:00
transform ( value : any ) { return ` ${ value } ${ value } ` ; }
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[emitter]' , outputs : [ 'event' ] } )
2015-12-04 11:02:19 -05:00
class DirectiveEmittingEvent {
2015-05-21 19:30:07 -04:00
msg : string ;
2015-10-24 21:48:43 -04:00
event : EventEmitter < any > ;
2015-05-21 19:30:07 -04:00
constructor ( ) {
this . msg = '' ;
this . event = new EventEmitter ( ) ;
}
2016-08-02 18:53:34 -04:00
fireEvent ( msg : string ) { this . event . emit ( msg ) ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[update-host-attributes]' , host : { 'role' : 'button' } } )
2015-05-21 19:30:07 -04:00
class DirectiveUpdatingHostAttributes {
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[update-host-properties]' , host : { '[id]' : 'id' } } )
2015-05-21 19:30:07 -04:00
class DirectiveUpdatingHostProperties {
id : string ;
2016-06-08 19:38:52 -04:00
constructor ( ) { this . id = 'one' ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listener]' , host : { '(event)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningEvent {
msg : string ;
constructor ( ) { this . msg = '' ; }
onEvent ( msg : string ) { this . msg = msg ; }
}
@Directive ( {
selector : '[listener]' ,
2015-06-09 06:33:40 -04:00
host : {
'(domEvent)' : 'onEvent($event.type)' ,
'(window:domEvent)' : 'onWindowEvent($event.type)' ,
'(document:domEvent)' : 'onDocumentEvent($event.type)' ,
'(body:domEvent)' : 'onBodyEvent($event.type)'
2015-05-21 19:30:07 -04:00
}
} )
class DirectiveListeningDomEvent {
2015-09-01 11:52:54 -04:00
eventTypes : string [ ] = [ ] ;
onEvent ( eventType : string ) { this . eventTypes . push ( eventType ) ; }
2016-06-08 19:38:52 -04:00
onWindowEvent ( eventType : string ) { this . eventTypes . push ( 'window_' + eventType ) ; }
onDocumentEvent ( eventType : string ) { this . eventTypes . push ( 'document_' + eventType ) ; }
onBodyEvent ( eventType : string ) { this . eventTypes . push ( 'body_' + eventType ) ; }
2015-05-21 19:30:07 -04:00
}
2016-11-12 08:08:58 -05:00
let globalCounter = 0 ;
2015-06-12 10:50:45 -04:00
@Directive ( { selector : '[listenerother]' , host : { '(window:domEvent)' : 'onEvent($event.type)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventOther {
eventType : string ;
constructor ( ) { this . eventType = '' ; }
onEvent ( eventType : string ) {
globalCounter ++ ;
2016-06-08 19:38:52 -04:00
this . eventType = 'other_' + eventType ;
2015-05-21 19:30:07 -04:00
}
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listenerprevent]' , host : { '(click)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventPrevent {
2016-06-14 17:53:01 -04:00
onEvent ( event : any ) { return false ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listenernoprevent]' , host : { '(click)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventNoPrevent {
2016-06-14 17:53:01 -04:00
onEvent ( event : any ) { return true ; }
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[id]' , inputs : [ 'id' ] } )
2015-05-21 19:30:07 -04:00
class IdDir {
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
id ! : string ;
2015-05-21 19:30:07 -04:00
}
2016-02-03 14:35:42 -05:00
@Directive ( { selector : '[customEvent]' } )
class EventDir {
@Output ( ) customEvent = new EventEmitter ( ) ;
doSomething() { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[static]' } )
class NeedsAttribute {
2016-06-14 17:53:01 -04:00
typeAttribute : string ;
staticAttribute : string ;
fooAttribute : string ;
2016-06-08 19:38:52 -04:00
constructor (
2016-06-14 17:53:01 -04:00
@Attribute ( 'type' ) typeAttribute : string , @Attribute ( 'static' ) staticAttribute : string ,
@Attribute ( 'foo' ) fooAttribute : string ) {
2015-05-21 19:30:07 -04:00
this . typeAttribute = typeAttribute ;
2015-08-20 04:12:46 -04:00
this . staticAttribute = staticAttribute ;
2015-05-21 19:30:07 -04:00
this . fooAttribute = fooAttribute ;
}
}
@Injectable ( )
class PublicApi {
}
2015-09-18 13:33:23 -04:00
@Directive ( {
selector : '[public-api]' ,
2016-07-30 22:18:14 -04:00
providers : [ { provide : PublicApi , useExisting : PrivateImpl , deps : [ ] } ]
2015-09-18 13:33:23 -04:00
} )
2015-05-21 19:30:07 -04:00
class PrivateImpl extends PublicApi {
}
@Directive ( { selector : '[needs-public-api]' } )
class NeedsPublicApi {
2015-07-29 14:26:09 -04:00
constructor ( @Host ( ) api : PublicApi ) { expect ( api instanceof PrivateImpl ) . toBe ( true ) ; }
2015-05-21 19:30:07 -04:00
}
2016-04-28 17:00:31 -04:00
class ToolbarContext {
constructor ( public toolbarProp : string ) { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[toolbarpart]' } )
class ToolbarPart {
2016-04-28 17:00:31 -04:00
templateRef : TemplateRef < ToolbarContext > ;
constructor ( templateRef : TemplateRef < ToolbarContext > ) { this . templateRef = templateRef ; }
2015-05-21 19:30:07 -04:00
}
2015-11-23 19:02:19 -05:00
@Directive ( { selector : '[toolbarVc]' , inputs : [ 'toolbarVc' ] } )
2015-05-21 19:30:07 -04:00
class ToolbarViewContainer {
2017-02-15 00:03:18 -05:00
constructor ( public vc : ViewContainerRef ) { }
2015-05-21 19:30:07 -04:00
set toolbarVc ( part : ToolbarPart ) {
2016-04-28 17:00:31 -04:00
this . vc . createEmbeddedView ( part . templateRef , new ToolbarContext ( 'From toolbar' ) , 0 ) ;
2015-05-21 19:30:07 -04:00
}
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'toolbar' ,
2016-04-25 22:52:24 -04:00
template : 'TOOLBAR(<div *ngFor="let part of query" [toolbarVc]="part"></div>)' ,
2015-05-21 19:30:07 -04:00
} )
class ToolbarComponent {
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
@ContentChildren ( ToolbarPart ) query ! : QueryList < ToolbarPart > ;
2017-02-15 00:03:18 -05:00
ctxProp : string = 'hello world' ;
2015-05-21 19:30:07 -04:00
2017-02-15 00:03:18 -05:00
constructor ( ) { }
2015-05-21 19:30:07 -04:00
}
2015-10-10 22:56:22 -04:00
@Directive ( { selector : '[two-way]' , inputs : [ 'control' ] , outputs : [ 'controlChange' ] } )
2015-05-21 19:30:07 -04:00
class DirectiveWithTwoWayBinding {
2015-10-10 22:56:22 -04:00
controlChange = new EventEmitter ( ) ;
2016-06-14 17:53:01 -04:00
control : any = null ;
2015-05-21 19:30:07 -04:00
2016-08-02 18:53:34 -04:00
triggerChange ( value : any ) { this . controlChange . emit ( value ) ; }
2015-05-21 19:30:07 -04:00
}
@Injectable ( )
class InjectableService {
}
2015-07-13 19:28:44 -04:00
function createInjectableWithLogging ( inj : Injector ) {
inj . get ( ComponentProvidingLoggingInjectable ) . created = true ;
return new InjectableService ( ) ;
}
@Component ( {
selector : 'component-providing-logging-injectable' ,
2016-07-30 22:18:14 -04:00
providers :
[ { provide : InjectableService , useFactory : createInjectableWithLogging , deps : [ Injector ] } ] ,
2016-03-08 16:36:48 -05:00
template : ''
2015-07-13 19:28:44 -04:00
} )
class ComponentProvidingLoggingInjectable {
created : boolean = false ;
}
2015-10-11 01:11:13 -04:00
@Directive ( { selector : 'directive-providing-injectable' , providers : [ [ InjectableService ] ] } )
2015-05-21 19:30:07 -04:00
class DirectiveProvidingInjectable {
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'directive-providing-injectable' ,
viewProviders : [ [ InjectableService ] ] ,
template : ''
} )
2015-05-21 19:30:07 -04:00
class DirectiveProvidingInjectableInView {
}
2015-07-29 18:01:22 -04:00
@Component ( {
selector : 'directive-providing-injectable' ,
2016-07-30 22:18:14 -04:00
providers : [ { provide : InjectableService , useValue : 'host' } ] ,
viewProviders : [ { provide : InjectableService , useValue : 'view' } ] ,
2016-03-08 16:36:48 -05:00
template : ''
2015-07-29 18:01:22 -04:00
} )
class DirectiveProvidingInjectableInHostAndView {
}
2015-06-04 06:15:34 -04:00
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'directive-consuming-injectable' , template : '' } )
2015-05-21 19:30:07 -04:00
class DirectiveConsumingInjectable {
2016-06-14 17:53:01 -04:00
injectable : any ;
2015-05-21 19:30:07 -04:00
2016-06-14 17:53:01 -04:00
constructor ( @Host ( ) @Inject ( InjectableService ) injectable : any ) { this . injectable = injectable ; }
2015-05-21 19:30:07 -04:00
}
@Component ( { selector : 'directive-containing-directive-consuming-an-injectable' } )
class DirectiveContainingDirectiveConsumingAnInjectable {
2016-06-14 17:53:01 -04:00
directive : any ;
2015-05-21 19:30:07 -04:00
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'directive-consuming-injectable-unbounded' , template : '' } )
2015-05-21 19:30:07 -04:00
class DirectiveConsumingInjectableUnbounded {
2016-06-14 17:53:01 -04:00
injectable : any ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
constructor (
injectable : InjectableService ,
@SkipSelf ( ) parent : DirectiveContainingDirectiveConsumingAnInjectable ) {
2015-05-21 19:30:07 -04:00
this . injectable = injectable ;
parent . directive = this ;
}
}
2015-05-21 19:56:02 -04:00
class EventBus {
parentEventBus : EventBus ;
name : string ;
constructor ( parentEventBus : EventBus , name : string ) {
this . parentEventBus = parentEventBus ;
this . name = name ;
}
}
@Directive ( {
selector : 'grand-parent-providing-event-bus' ,
2017-03-29 12:34:45 -04:00
providers : [ { provide : EventBus , useValue : new EventBus ( null ! , 'grandparent' ) } ]
2015-05-21 19:56:02 -04:00
} )
class GrandParentProvidingEventBus {
bus : EventBus ;
constructor ( bus : EventBus ) { this . bus = bus ; }
}
2016-06-14 17:53:01 -04:00
function createParentBus ( peb : EventBus ) {
2016-06-08 19:38:52 -04:00
return new EventBus ( peb , 'parent' ) ;
2015-05-21 19:56:02 -04:00
}
@Component ( {
selector : 'parent-providing-event-bus' ,
2016-09-12 22:14:17 -04:00
providers : [ { provide : EventBus , useFactory : createParentBus , deps : [ [ EventBus , new SkipSelf ( ) ] ] } ] ,
2016-06-02 20:30:40 -04:00
template : ` <child-consuming-event-bus></child-consuming-event-bus> `
2015-05-21 19:56:02 -04:00
} )
class ParentProvidingEventBus {
bus : EventBus ;
grandParentBus : EventBus ;
2015-07-29 14:26:09 -04:00
constructor ( bus : EventBus , @SkipSelf ( ) grandParentBus : EventBus ) {
2015-05-21 19:56:02 -04:00
this . bus = bus ;
this . grandParentBus = grandParentBus ;
}
}
@Directive ( { selector : 'child-consuming-event-bus' } )
class ChildConsumingEventBus {
bus : EventBus ;
2015-07-29 14:26:09 -04:00
constructor ( @SkipSelf ( ) bus : EventBus ) { this . bus = bus ; }
2015-05-21 19:56:02 -04:00
}
2015-06-03 14:02:51 -04:00
2015-11-23 19:02:19 -05:00
@Directive ( { selector : '[someImpvp]' , inputs : [ 'someImpvp' ] } )
2015-06-03 14:02:51 -04:00
class SomeImperativeViewport {
2017-03-29 12:34:45 -04:00
view : EmbeddedViewRef < Object > | null ;
2016-06-14 17:53:01 -04:00
anchor : any ;
2016-06-08 19:38:52 -04:00
constructor (
public vc : ViewContainerRef , public templateRef : TemplateRef < Object > ,
2016-06-14 17:53:01 -04:00
@Inject ( ANCHOR_ELEMENT ) anchor : any ) {
2015-06-03 14:02:51 -04:00
this . view = null ;
this . anchor = anchor ;
}
set someImpvp ( value : boolean ) {
2017-02-15 00:03:18 -05:00
if ( this . view ) {
2015-06-16 12:45:03 -04:00
this . vc . clear ( ) ;
2015-06-03 14:02:51 -04:00
this . view = null ;
}
2017-02-15 00:03:18 -05:00
2015-06-03 14:02:51 -04:00
if ( value ) {
2015-07-17 11:03:40 -04:00
this . view = this . vc . createEmbeddedView ( this . templateRef ) ;
2016-11-12 08:08:58 -05:00
const nodes = this . view . rootNodes ;
for ( let i = 0 ; i < nodes . length ; i ++ ) {
2016-04-28 20:50:03 -04:00
getDOM ( ) . appendChild ( this . anchor , nodes [ i ] ) ;
2015-06-03 14:02:51 -04:00
}
}
}
}
2015-06-04 16:45:08 -04:00
@Directive ( { selector : '[export-dir]' , exportAs : 'dir' } )
class ExportDir {
2015-06-09 06:33:40 -04:00
}
2015-06-16 12:45:03 -04:00
2017-08-15 19:34:47 -04:00
@Directive ( { selector : '[multiple-export-as]' , exportAs : 'dirX, dirY' } )
export class DirectiveWithMultipleExportAsNames {
}
2015-06-16 12:45:03 -04:00
@Component ( { selector : 'comp' } )
class ComponentWithoutView {
}
2015-06-16 14:16:08 -04:00
@Directive ( { selector : '[no-duplicate]' } )
class DuplicateDir {
2015-07-28 09:56:18 -04:00
constructor ( elRef : ElementRef ) {
2016-04-28 20:50:03 -04:00
getDOM ( ) . setText ( elRef . nativeElement , getDOM ( ) . getText ( elRef . nativeElement ) + 'noduplicate' ) ;
2015-06-16 14:16:08 -04:00
}
}
@Directive ( { selector : '[no-duplicate]' } )
class OtherDuplicateDir {
2015-07-28 09:56:18 -04:00
constructor ( elRef : ElementRef ) {
2016-06-08 19:38:52 -04:00
getDOM ( ) . setText (
elRef . nativeElement , getDOM ( ) . getText ( elRef . nativeElement ) + 'othernoduplicate' ) ;
2015-06-16 14:16:08 -04:00
}
}
2015-07-22 15:00:35 -04:00
@Directive ( { selector : 'directive-throwing-error' } )
class DirectiveThrowingAnError {
2016-08-25 03:50:16 -04:00
constructor ( ) { throw new Error ( 'BOOM' ) ; }
2015-07-22 15:00:35 -04:00
}
2015-09-03 18:10:48 -04:00
2015-10-06 20:03:37 -04:00
@Component ( {
selector : 'component-with-template' ,
2016-04-25 22:52:24 -04:00
template : ` No View Decorator: <div *ngFor="let item of items">{{item}}</div> `
2015-10-06 20:03:37 -04:00
} )
2015-10-06 22:14:45 -04:00
class ComponentWithTemplate {
2015-10-06 20:03:37 -04:00
items = [ 1 , 2 , 3 ] ;
}
2015-09-03 18:10:48 -04:00
@Directive ( { selector : 'with-prop-decorators' } )
class DirectiveWithPropDecorators {
2016-06-14 17:53:01 -04:00
target : any ;
2015-09-04 17:07:16 -04:00
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
@Input ( 'elProp' ) dirProp ! : string ;
2015-09-30 23:59:23 -04:00
@Output ( 'elEvent' ) event = new EventEmitter ( ) ;
2015-09-03 18:10:48 -04:00
2018-06-18 19:38:33 -04:00
// TODO(issue/24571): remove '!'.
@HostBinding ( 'attr.my-attr' ) myAttr ! : string ;
2016-06-08 19:38:52 -04:00
@HostListener ( 'click' , [ '$event.target' ] )
2016-06-14 17:53:01 -04:00
onClick ( target : any ) { this . target = target ; }
2015-09-04 17:07:16 -04:00
2016-08-02 18:53:34 -04:00
fireEvent ( msg : any ) { this . event . emit ( msg ) ; }
2015-09-11 16:45:31 -04:00
}
2016-01-08 15:01:29 -05:00
@Component ( { selector : 'some-cmp' } )
class SomeCmp {
value : any ;
}
2017-02-08 06:36:43 -05:00
@Component ( {
selector : 'parent-cmp' ,
template : ` <cmp [test $ ]="name"></cmp> ` ,
} )
export class ParentCmp {
name : string = 'hello' ;
}
@Component ( { selector : 'cmp' , template : '' } )
class SomeCmpWithInput {
@Input ( ) test$ : any ;
}