Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions packages/compiler/src/metadata_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,12 +823,13 @@ export class CompileMetadataResolver {
this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
} else {
provider = resolveForwardRef(provider);
if (isValidType(provider)) {
provider = {provide: provider, useClass: provider};
}
let providerMeta: cpl.ProviderMeta;
if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) {
this._validateProvider(provider);
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
} else if (isValidType(provider)) {
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
} else if (provider === void 0) {
this._reportError(syntaxError(
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
Expand Down Expand Up @@ -863,12 +864,18 @@ export class CompileMetadataResolver {
}

private _validateProvider(provider: any): void {
if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
this._reportError(syntaxError(
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
if (provider.hasOwnProperty('useClass')) {
const identifier = resolveForwardRef(provider.useClass);
if (identifier == null) {
this._reportError(syntaxError(
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${identifier}.
Usually it happens when:
1. There's a circular dependency (might be caused by using index.ts (barrel) files).
2. Class was used before it was declared. Use forwardRef in this case.`));
} else if (!this.isInjectable(identifier)) {
this._reportError(
syntaxError(`${stringifyType(identifier)} must be annotated with @Injectable`));
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/test/metadata_resolver_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ export function main() {
}

expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
.toThrowError(`NonAnnotatedService must be annotated with @Injectable`);
}));

it('should throw with descriptive error message when encounter invalid provider',
Expand Down
4 changes: 3 additions & 1 deletion packages/core/test/forward_ref_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {Component, ContentChildren, Directive, Inject, NO_ERRORS_SCHEMA, NgModule, QueryList, asNativeElements, forwardRef} from '@angular/core';
import {Component, ContentChildren, Directive, Inject, Injectable, NO_ERRORS_SCHEMA, NgModule, QueryList, asNativeElements, forwardRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';

Expand Down Expand Up @@ -52,10 +52,12 @@ class Door {
constructor(@Inject(forwardRef(() => Frame)) frame: Frame) { this.frame = frame; }
}

@Injectable()
class Frame {
name: string = 'frame';
}

@Injectable()
class ModuleFrame {
name: string = 'moduleFram';
}
Expand Down
28 changes: 6 additions & 22 deletions packages/core/test/linker/ng_module_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,18 @@ import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Componen
import {Console} from '@angular/core/src/console';
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';

import {NgModuleInjector} from '../../src/linker/ng_module_factory';
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
import {stringify} from '../../src/util';

class Engine {}

class BrokenEngine {
constructor() { throw new Error('Broken Engine'); }
@Injectable()
class Engine {
}

class DashboardSoftware {}

@Injectable()
class Dashboard {
constructor(software: DashboardSoftware) {}
class TurboEngine extends Engine {
}

class TurboEngine extends Engine {}

const CARS = new InjectionToken<Car[]>('Cars');
@Injectable()
class Car {
Expand All @@ -43,16 +35,6 @@ class CarWithOptionalEngine {
constructor(@Optional() engine: Engine) { this.engine = engine; }
}

@Injectable()
class CarWithDashboard {
engine: Engine;
dashboard: Dashboard;
constructor(engine: Engine, dashboard: Dashboard) {
this.engine = engine;
this.dashboard = dashboard;
}
}

@Injectable()
class SportsCar extends Car {
engine: Engine;
Expand Down Expand Up @@ -638,7 +620,7 @@ function declareTests({useJit}: {useJit: boolean}) {

it('should throw when no type and not @Inject (class case)', () => {
expect(() => createInjector([NoAnnotations]))
.toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).');
.toThrowError('NoAnnotations must be annotated with @Injectable');
});

it('should throw when no type and not @Inject (factory case)', () => {
Expand Down Expand Up @@ -901,6 +883,7 @@ function declareTests({useJit}: {useJit: boolean}) {
it('should support ngOnDestroy on any provider', () => {
let destroyed = false;

@Injectable()
class SomeInjectable {
ngOnDestroy() { destroyed = true; }
}
Expand All @@ -918,6 +901,7 @@ function declareTests({useJit}: {useJit: boolean}) {
it('should instantiate providers with lifecycle eagerly', () => {
let created = false;

@Injectable()
class SomeInjectable {
constructor() { created = true; }
ngOnDestroy() {}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/test/linker/view_injector_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, InjectionToken, Input, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, Injectable, InjectionToken, Input, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/src/matchers';
Expand Down Expand Up @@ -360,6 +360,7 @@ export function main() {

it('should instantiate providers with a lifecycle hook eagerly', () => {
let created = false;
@Injectable()
class SomeInjectable {
constructor() { created = true; }
ngOnDestroy() {}
Expand Down
8 changes: 6 additions & 2 deletions packages/examples/core/di/ts/metadata_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ export function main() {
describe('Host', () => {
it('works', () => {
// #docregion Host
class OtherService {}
class HostService {}
@Injectable()
class OtherService {
}
@Injectable()
class HostService {
}

@Directive({selector: 'child-directive'})
class ChildDirective {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationEvent, AnimationMetadataType, AnimationPlayer, AnimationStateMetadata, AnimationTriggerMetadata, ɵStyleData} from '@angular/animations';

import {Injectable} from '@angular/core';
import {AnimationEngine} from '../animation_engine';
import {copyStyles, eraseStyles, normalizeStyles, setStyles} from '../util';
import {eraseStyles, normalizeStyles, setStyles} from '../util';

interface ListenerTuple {
eventPhase: string;
Expand All @@ -27,6 +27,7 @@ interface ChangeTuple {
const DEFAULT_STATE_VALUE = 'void';
const DEFAULT_STATE_STYLES = '*';

@Injectable()
export class NoopAnimationEngine extends AnimationEngine {
private _listeners = new Map<any, ListenerTuple[]>();
private _changes: ChangeTuple[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AUTO_STYLE, AnimationPlayer, NoopAnimationPlayer, ɵStyleData} from '@angular/animations';

import {Injectable} from '@angular/core';
import {AnimationDriver} from '../../src/render/animation_driver';

/**
* @experimental Animation support is experimental.
*/
@Injectable()
export class MockAnimationDriver implements AnimationDriver {
static log: AnimationPlayer[] = [];

Expand Down
7 changes: 2 additions & 5 deletions packages/platform-browser/test/testing_public_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,8 @@ class ChildChildComp {
selector: 'child-comp',
template: `<span>Original {{childBinding}}(<child-child-comp></child-child-comp>)</span>`,
})
@Injectable()
class ChildWithChildComp {
childBinding: string;
constructor() { this.childBinding = 'Child'; }
}

@Injectable()
class FancyService {
value: string = 'real value';
getAsyncValue() { return Promise.resolve('async value'); }
Expand All @@ -62,6 +58,7 @@ class FancyService {
}
}

@Injectable()
class MockFancyService extends FancyService {
value: string = 'mocked out value';
}
Expand Down
2 changes: 2 additions & 0 deletions packages/router/src/router_outlet_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable} from '@angular/core';
import {RouterOutlet} from './directives/router_outlet';

/**
* @whatItDoes Contains all the router outlets created in a component.
*
* @stable
*/
@Injectable()
export class RouterOutletMap {
/** @internal */
_outlets: {[name: string]: RouterOutlet} = {};
Expand Down
2 changes: 2 additions & 0 deletions packages/router/src/router_preloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export abstract class PreloadingStrategy {
*
* @experimental
*/
@Injectable()
export class PreloadAllModules implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> {
return _catch.call(fn(), () => of (null));
Expand All @@ -56,6 +57,7 @@ export class PreloadAllModules implements PreloadingStrategy {
*
* @experimental
*/
@Injectable()
export class NoPreloading implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> { return of (null); }
}
Expand Down
2 changes: 2 additions & 0 deletions packages/router/src/url_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injectable} from '@angular/core';
import {PRIMARY_OUTLET} from './shared';
import {forEach, shallowEqual} from './utils/collection';

Expand Down Expand Up @@ -257,6 +258,7 @@ export abstract class UrlSerializer {
*
* @stable
*/
@Injectable()
export class DefaultUrlSerializer implements UrlSerializer {
/** Parses a url into a {@link UrlTree} */
parse(url: string): UrlTree {
Expand Down
3 changes: 2 additions & 1 deletion packages/router/test/bootstrap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {APP_BASE_HREF} from '@angular/common';
import {ApplicationRef, CUSTOM_ELEMENTS_SCHEMA, Component, NgModule, destroyPlatform} from '@angular/core';
import {ApplicationRef, CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, NgModule, destroyPlatform} from '@angular/core';
import {inject} from '@angular/core/testing';
import {BrowserModule, DOCUMENT, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
Expand All @@ -27,6 +27,7 @@ describe('bootstrap', () => {
class SecondRootCmp {
}

@Injectable()
class TestResolver implements Resolve<any> {
resolve() {
let resolve: any = null;
Expand Down
16 changes: 11 additions & 5 deletions packages/router/test/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@
*/

import {CommonModule, Location} from '@angular/common';
import {Component, NgModule, NgModuleFactoryLoader} from '@angular/core';
import {Component, Injectable, NgModule, NgModuleFactoryLoader} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {Observable} from 'rxjs/Observable';
import {map} from 'rxjs/operator/map';

import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, Params, PreloadAllModules, PreloadingStrategy, Resolve, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterStateSnapshot, RoutesRecognized, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '../index';
import {RouterPreloader} from '../src/router_preloader';
import {forEach, shallowEqual} from '../src/utils/collection';
import {forEach} from '../src/utils/collection';
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';

describe('Integration', () => {
Expand Down Expand Up @@ -844,6 +843,7 @@ describe('Integration', () => {
})));

describe('data', () => {
@Injectable()
class ResolveSix implements Resolve<number> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): number { return 6; }
}
Expand Down Expand Up @@ -1391,6 +1391,7 @@ describe('Integration', () => {
});

describe('should work when given a class', () => {
@Injectable()
class AlwaysTrue implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return true;
Expand Down Expand Up @@ -1870,6 +1871,7 @@ describe('Integration', () => {
describe('next state', () => {
let log: string[];

@Injectable()
class ClassWithNextState implements CanDeactivate<TeamCmp> {
canDeactivate(
component: TeamCmp, currentRoute: ActivatedRouteSnapshot,
Expand Down Expand Up @@ -1932,6 +1934,7 @@ describe('Integration', () => {
});

describe('should work when given a class', () => {
@Injectable()
class AlwaysTrue implements CanDeactivate<TeamCmp> {
canDeactivate(
component: TeamCmp, route: ActivatedRouteSnapshot,
Expand Down Expand Up @@ -2169,6 +2172,7 @@ describe('Integration', () => {

describe('order', () => {

@Injectable()
class Logger {
logs: string[] = [];
add(thing: string) { this.logs.push(thing); }
Expand Down Expand Up @@ -2546,8 +2550,9 @@ describe('Integration', () => {


describe('should use the injector of the lazily-loaded configuration', () => {
class LazyLoadedServiceDefinedInModule {}
class LazyLoadedServiceDefinedInCmp {}
@Injectable()
class LazyLoadedServiceDefinedInModule {
}

@Component({
selector: 'eager-parent',
Expand Down Expand Up @@ -2775,6 +2780,7 @@ describe('Integration', () => {
});

describe('custom url handling strategies', () => {
@Injectable()
class CustomUrlHandlingStrategy implements UrlHandlingStrategy {
shouldProcessUrl(url: UrlTree): boolean {
return url.toString().startsWith('/include') || url.toString() === '/';
Expand Down
4 changes: 2 additions & 2 deletions packages/upgrade/src/common/content_projection_helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Type} from '@angular/core';
import {Injectable, Type} from '@angular/core';
import * as angular from './angular1';


@Injectable()
export class ContentProjectionHelper {
groupProjectableNodes($injector: angular.IInjectorService, component: Type<any>, nodes: Node[]):
Node[][] {
Expand Down
Loading