parent
1f96a93f59
commit
824ea8406c
|
@ -14,6 +14,12 @@ export interface ComponentInfo {
|
||||||
outputs?: string[];
|
outputs?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `PropertyBinding` represents a mapping between a property name
|
||||||
|
* and an attribute name. It is parsed from a string of the form
|
||||||
|
* `"prop: attr"`; or simply `"propAndAttr" where the property
|
||||||
|
* and attribute have the same identifier.
|
||||||
|
*/
|
||||||
export class PropertyBinding {
|
export class PropertyBinding {
|
||||||
prop: string;
|
prop: string;
|
||||||
attr: string;
|
attr: string;
|
||||||
|
|
|
@ -16,6 +16,50 @@ import {DowngradeComponentAdapter} from './downgrade_component_adapter';
|
||||||
let downgradeCount = 0;
|
let downgradeCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @whatItDoes
|
||||||
|
*
|
||||||
|
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
|
||||||
|
* library for hybrid upgrade apps that support AoT compilation*
|
||||||
|
*
|
||||||
|
* Allows an Angular 2+ component to be used from Angular 1.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* Let's assume that you have an Angular 2+ component called `ng2Heroes` that needs
|
||||||
|
* to be made available in Angular 1 templates.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng2-heroes"}
|
||||||
|
*
|
||||||
|
* We must create an Angular 1 [directive](https://docs.angularjs.org/guide/directive)
|
||||||
|
* that will make this Angular 2+ component available inside Angular 1 templates.
|
||||||
|
* The `downgradeComponent()` function returns a factory function that we
|
||||||
|
* can use to define the Angular 1 directive that wraps the "downgraded" component.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng2-heroes-wrapper"}
|
||||||
|
*
|
||||||
|
* In this example you can see that we must provide information about the component being
|
||||||
|
* "downgraded". This is because once the AoT compiler has run, all metadata about the
|
||||||
|
* component has been removed from the code, and so cannot be inferred.
|
||||||
|
*
|
||||||
|
* We must do the following:
|
||||||
|
* * specify the Angular 2+ component class that is to be downgraded
|
||||||
|
* * specify all inputs and outputs that the Angular 1 component expects
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* A helper function that returns a factory function to be used for registering an
|
||||||
|
* Angular 1 wrapper directive for "downgrading" an Angular 2+ component.
|
||||||
|
*
|
||||||
|
* The parameter contains information about the Component that is being downgraded:
|
||||||
|
*
|
||||||
|
* * `component: Type<any>`: The type of the Component that will be downgraded
|
||||||
|
* * `inputs: string[]`: A collection of strings that specify what inputs the component accepts.
|
||||||
|
* * `outputs: string[]`: A collection of strings that specify what outputs the component emits.
|
||||||
|
*
|
||||||
|
* The `inputs` and `outputs` are strings that map the names of properties to camelCased
|
||||||
|
* attribute names. They are of the form `"prop: attr"`; or simply `"propAndAttr" where the
|
||||||
|
* property and attribute have the same identifier.
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export function downgradeComponent(info: /* ComponentInfo */ {
|
export function downgradeComponent(info: /* ComponentInfo */ {
|
||||||
|
|
|
@ -10,14 +10,44 @@ import {Injector} from '@angular/core';
|
||||||
import {INJECTOR_KEY} from './constants';
|
import {INJECTOR_KEY} from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an Angular 1 factory that will return an Angular 2 injectable thing
|
* @whatItDoes
|
||||||
* (e.g. service, pipe, component, etc)
|
|
||||||
*
|
*
|
||||||
* Usage:
|
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
|
||||||
|
* library for hybrid upgrade apps that support AoT compilation*
|
||||||
*
|
*
|
||||||
* ```
|
* Allow an Angular 2+ service to be accessible from Angular 1.
|
||||||
* angular1Module.factory('someService', downgradeInjectable(SomeService))
|
*
|
||||||
* ```
|
* @howToUse
|
||||||
|
*
|
||||||
|
* First ensure that the service to be downgraded is provided in an {@link NgModule}
|
||||||
|
* that will be part of the upgrade application. For example, let's assume we have
|
||||||
|
* defined `HeroesService`
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng2-heroes-service"}
|
||||||
|
*
|
||||||
|
* and that we have included this in our upgrade app {@link NgModule}
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng2-module"}
|
||||||
|
*
|
||||||
|
* Now we can register the `downgradeInjectable` factory function for the service
|
||||||
|
* on an Angular 1 module.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="downgrade-ng2-heroes-service"}
|
||||||
|
*
|
||||||
|
* Inside an Angular 1 component's controller we can get hold of the
|
||||||
|
* downgraded service via the name we gave when downgrading.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="example-app"}
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Takes a `token` that identifies a service provided from Angular 2+.
|
||||||
|
*
|
||||||
|
* Returns a [factory function](https://docs.angularjs.org/guide/di) that can be
|
||||||
|
* used to register the service on an Angular 1 module.
|
||||||
|
*
|
||||||
|
* The factory function provides access to the Angular 2+ service that
|
||||||
|
* is identified by the `token` parameter.
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,6 +43,43 @@ interface IControllerInstance extends IBindingDestination {
|
||||||
type LifecycleHook = '$onChanges' | '$onDestroy' | '$onInit' | '$postLink';
|
type LifecycleHook = '$onChanges' | '$onDestroy' | '$onInit' | '$postLink';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @whatItDoes
|
||||||
|
*
|
||||||
|
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
|
||||||
|
* library for hybrid upgrade apps that support AoT compilation*
|
||||||
|
*
|
||||||
|
* Allows an Angular 1 component to be used from Angular 2+.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* Let's assume that you have an Angular 1 component called `ng1Hero` that needs
|
||||||
|
* to be made available in Angular 2+ templates.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng1-hero"}
|
||||||
|
*
|
||||||
|
* We must create a {@link Directive} that will make this Angular 1 component
|
||||||
|
* available inside Angular 2+ templates.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng1-hero-wrapper"}
|
||||||
|
*
|
||||||
|
* In this example you can see that we must derive from the {@link UpgradeComponent}
|
||||||
|
* base class but also provide an {@link Directive `@Directive`} decorator. This is
|
||||||
|
* because the AoT compiler requires that this information is statically available at
|
||||||
|
* compile time.
|
||||||
|
*
|
||||||
|
* Note that we must do the following:
|
||||||
|
* * specify the directive's selector (`ng1-hero`)
|
||||||
|
* * specify all inputs and outputs that the Angular 1 component expects
|
||||||
|
* * derive from `UpgradeComponent`
|
||||||
|
* * call the base class from the constructor, passing
|
||||||
|
* * the Angular 1 name of the component (`ng1Hero`)
|
||||||
|
* * the {@link ElementRef} and {@link Injector} for the component wrapper
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* A helper class that should be used as a base class for creating Angular directives
|
||||||
|
* that wrap Angular 1 components that need to be "upgraded".
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
||||||
|
@ -63,6 +100,22 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
||||||
private controllerInstance: IControllerInstance = null;
|
private controllerInstance: IControllerInstance = null;
|
||||||
private bindingDestination: IBindingDestination = null;
|
private bindingDestination: IBindingDestination = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new `UpgradeComponent` instance. You should not normally need to do this.
|
||||||
|
* Instead you should derive a new class from this one and call the super constructor
|
||||||
|
* from the base class.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng1-hero-wrapper" }
|
||||||
|
*
|
||||||
|
* * The `name` parameter should be the name of the Angular 1 directive.
|
||||||
|
* * The `elementRef` and `injector` parameters should be acquired from Angular by dependency
|
||||||
|
* injection into the base class constructor.
|
||||||
|
*
|
||||||
|
* Note that we must manually implement lifecycle hooks that call through to the super class.
|
||||||
|
* This is because, at the moment, the AoT compiler is not able to tell that the
|
||||||
|
* `UpgradeComponent`
|
||||||
|
* already implements them and so does not wire up calls to them at runtime.
|
||||||
|
*/
|
||||||
constructor(private name: string, private elementRef: ElementRef, private injector: Injector) {
|
constructor(private name: string, private elementRef: ElementRef, private injector: Injector) {
|
||||||
this.$injector = injector.get($INJECTOR);
|
this.$injector = injector.get($INJECTOR);
|
||||||
this.$compile = this.$injector.get($COMPILE);
|
this.$compile = this.$injector.get($COMPILE);
|
||||||
|
@ -77,7 +130,7 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
||||||
this.bindings = this.initializeBindings(this.directive);
|
this.bindings = this.initializeBindings(this.directive);
|
||||||
this.linkFn = this.compileTemplate(this.directive);
|
this.linkFn = this.compileTemplate(this.directive);
|
||||||
|
|
||||||
// We ask for the Angular 1 scope from the Angular 2 injector, since
|
// We ask for the Angular 1 scope from the Angular 2+ injector, since
|
||||||
// we will put the new component scope onto the new injector for each component
|
// we will put the new component scope onto the new injector for each component
|
||||||
const $parentScope = injector.get($SCOPE);
|
const $parentScope = injector.get($SCOPE);
|
||||||
// QUESTION 1: Should we create an isolated scope if the scope is only true?
|
// QUESTION 1: Should we create an isolated scope if the scope is only true?
|
||||||
|
|
|
@ -17,15 +17,130 @@ import {$$TESTABILITY, $DELEGATE, $INJECTOR, $PROVIDE, $ROOT_SCOPE, INJECTOR_KEY
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Ng1Module contains providers for the Ng1Adapter and all the core Angular 1 services;
|
* @whatItDoes
|
||||||
* and also holds the `bootstrapNg1()` method fo bootstrapping an upgraded Angular 1 app.
|
*
|
||||||
|
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
|
||||||
|
* library for hybrid upgrade apps that support AoT compilation*
|
||||||
|
*
|
||||||
|
* Allows Angular 1 and Angular 2+ components to be used together inside a hybrid upgrade
|
||||||
|
* application, which supports AoT compilation.
|
||||||
|
*
|
||||||
|
* Specifically, the classes and functions in the `upgrade/static` module allow the following:
|
||||||
|
* 1. Creation of an Angular 2+ directive that wraps and exposes an Angular 1 component so
|
||||||
|
* that it can be used in an Angular 2 template. See {@link UpgradeComponent}.
|
||||||
|
* 2. Creation of an Angular 1 directive that wraps and exposes an Angular 2+ component so
|
||||||
|
* that it can be used in an Angular 1 template. See {@link downgradeComponent}.
|
||||||
|
* 3. Creation of an Angular 2+ root injector provider that wraps and exposes an Angular 1
|
||||||
|
* service so that it can be injected into an Angular 2+ context. See
|
||||||
|
* {@link UpgradeModule#upgrading-an-angular-1-service Upgrading an Angular 1 service} below.
|
||||||
|
* 4. Creation of an Angular 1 service that wraps and exposes an Angular 2+ injectable
|
||||||
|
* so that it can be injected into an Angular 1 context. See {@link downgradeInjectable}.
|
||||||
|
* 3. Bootstrapping of a hybrid Angular application which contains both of the frameworks
|
||||||
|
* coexisting in a single application. See the
|
||||||
|
* {@link UpgradeModule#example example} below.
|
||||||
|
*
|
||||||
|
* ## Mental Model
|
||||||
|
*
|
||||||
|
* When reasoning about how a hybrid application works it is useful to have a mental model which
|
||||||
|
* describes what is happening and explains what is happening at the lowest level.
|
||||||
|
*
|
||||||
|
* 1. There are two independent frameworks running in a single application, each framework treats
|
||||||
|
* the other as a black box.
|
||||||
|
* 2. Each DOM element on the page is owned exactly by one framework. Whichever framework
|
||||||
|
* instantiated the element is the owner. Each framework only updates/interacts with its own
|
||||||
|
* DOM elements and ignores others.
|
||||||
|
* 3. Angular 1 directives always execute inside the Angular 1 framework codebase regardless of
|
||||||
|
* where they are instantiated.
|
||||||
|
* 4. Angular 2+ components always execute inside the Angular 2+ framework codebase regardless of
|
||||||
|
* where they are instantiated.
|
||||||
|
* 5. An Angular 1 component can be "upgraded"" to an Angular 2+ component. This is achieved by
|
||||||
|
* defining an Angular 2+ directive, which bootstraps the Angular 1 component at its location
|
||||||
|
* in the DOM. See {@link UpgradeComponent}.
|
||||||
|
* 6. An Angular 2+ component can be "downgraded"" to an Angular 1 component. This is achieved by
|
||||||
|
* defining an Angular 1 directive, which bootstraps the Angular 2+ component at its location
|
||||||
|
* in the DOM. See {@link downgradeComponent}.
|
||||||
|
* 7. Whenever an "upgraded"/"downgraded" component is instantiated the host element is owned by
|
||||||
|
* the framework doing the instantiation. The other framework then instantiates and owns the
|
||||||
|
* view for that component.
|
||||||
|
* a. This implies that the component bindings will always follow the semantics of the
|
||||||
|
* instantiation framework.
|
||||||
|
* b. The DOM attributes are parsed by the framework that owns the current template. So
|
||||||
|
* attributes
|
||||||
|
* in Angular 1 templates must use kebab-case, while Angular 1 templates must use camelCase.
|
||||||
|
* c. However the template binding syntax will always use the Angular 2+ style, e.g. square
|
||||||
|
* brackets (`[...]`) for property binding.
|
||||||
|
* 8. Angular 1 is always bootstrapped first and owns the root component.
|
||||||
|
* 9. The new application is running in an Angular 2+ zone, and therefore it no longer needs calls
|
||||||
|
* to
|
||||||
|
* `$apply()`.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* `import {UpgradeModule} from '@angular/upgrade/static';`
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
* Import the {@link UpgradeModule} into your top level {@link NgModule Angular 2+ `NgModule`}.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region='ng2-module'}
|
||||||
|
*
|
||||||
|
* Then bootstrap the hybrid upgrade app's module, get hold of the {@link UpgradeModule} instance
|
||||||
|
* and use it to bootstrap the top level [Angular 1
|
||||||
|
* module](https://docs.angularjs.org/api/ng/type/angular.Module).
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region='bootstrap'}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Upgrading an Angular 1 service
|
||||||
|
*
|
||||||
|
* There is no specific API for upgrading an Angular 1 service. Instead you should just follow the
|
||||||
|
* following recipe:
|
||||||
|
*
|
||||||
|
* Let's say you have an Angular 1 service:
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="ng1-title-case-service"}
|
||||||
|
*
|
||||||
|
* Then you should define an Angular 2+ provider to be included in your {@link NgModule} `providers`
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="upgrade-ng1-service"}
|
||||||
|
*
|
||||||
|
* Then you can use the "upgraded" Angular 1 service by injecting it into an Angular 2 component
|
||||||
|
* or service.
|
||||||
|
*
|
||||||
|
* {@example upgrade/static/ts/module.ts region="use-ng1-upgraded-service"}
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* This class is an `NgModule`, which you import to provide Angular 1 core services,
|
||||||
|
* and has an instance method used to bootstrap the hybrid upgrade application.
|
||||||
|
*
|
||||||
|
* ## Core Angular 1 services
|
||||||
|
* Importing this {@link NgModule} will add providers for the core
|
||||||
|
* [Angular 1 services](https://docs.angularjs.org/api/ng/service) to the root injector.
|
||||||
|
*
|
||||||
|
* ## Bootstrap
|
||||||
|
* The runtime instance of this class contains a {@link UpgradeModule#bootstrap `bootstrap()`}
|
||||||
|
* method, which you use to bootstrap the top level Angular 1 module onto an element in the
|
||||||
|
* DOM for the hybrid upgrade app.
|
||||||
|
*
|
||||||
|
* It also contains properties to access the {@link UpgradeModule#injector root injector}, the
|
||||||
|
* bootstrap {@link NgZone} and the
|
||||||
|
* [Angular 1 $injector](https://docs.angularjs.org/api/auto/service/$injector).
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@NgModule({providers: angular1Providers})
|
@NgModule({providers: angular1Providers})
|
||||||
export class UpgradeModule {
|
export class UpgradeModule {
|
||||||
|
/**
|
||||||
|
* The Angular 1 `$injector` for the upgrade application.
|
||||||
|
*/
|
||||||
public $injector: any /*angular.IInjectorService*/;
|
public $injector: any /*angular.IInjectorService*/;
|
||||||
|
|
||||||
constructor(public injector: Injector, public ngZone: NgZone) {}
|
constructor(
|
||||||
|
/** The root {@link Injector} for the upgrade application. */
|
||||||
|
public injector: Injector,
|
||||||
|
/** The bootstrap zone for the upgrade application */
|
||||||
|
public ngZone: NgZone) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap an Angular 1 application from this NgModule
|
* Bootstrap an Angular 1 application from this NgModule
|
||||||
|
|
Loading…
Reference in New Issue