[Core] Allow passing additional props to ReactContent when creating render inputs

This commit is contained in:
Ben Grynhaus 2018-07-17 15:39:13 +03:00
Родитель 48b94bc594
Коммит 33f9e6c2d6
4 изменённых файлов: 57 добавлений и 28 удалений

Просмотреть файл

@ -1,7 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/package.schema.json",
"name": "@angular-react/core",
"version": "0.2.3",
"version": "0.2.4",
"ngPackage": {
"lib": {
"languageLevel": [

Просмотреть файл

@ -16,6 +16,7 @@ import toStyle from 'css-to-style';
import { isReactNode } from '../renderer/react-node';
import { renderComponent, renderFunc, renderTemplate } from '../renderer/renderprop-helpers';
import { ExternalReactContentProps } from '../renderer/react-content';
import { unreachable } from '../utils/types/unreachable';
type AttributeNameAlternative = [string, string | undefined];
@ -133,24 +134,26 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
/**
* Create an JSX renderer for an `@Input` property.
* @param input The input property
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
*/
protected createInputJsxRenderer<TContext extends object>(
input: InputRendererOptions<TContext>
input: InputRendererOptions<TContext>,
additionalProps?: ExternalReactContentProps
): JsxRenderFunc<TContext> | undefined {
if (input === undefined) {
return undefined;
}
if (input instanceof TemplateRef) {
return (context: TContext) => renderTemplate(input, context);
return (context: TContext) => renderTemplate(input, context, additionalProps);
}
if (input instanceof ComponentRef) {
return (context: TContext) => renderComponent(input, context);
return (context: TContext) => renderComponent(input, context, additionalProps);
}
if (input instanceof Function) {
return (context: TContext) => renderFunc(input, context);
return (context: TContext) => renderFunc(input, context, additionalProps);
}
if (typeof input === 'object') {
@ -158,7 +161,7 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
const componentFactory = factoryResolver.resolveComponentFactory(componentType);
const componentRef = componentFactory.create(injector);
return (context: TContext) => renderComponent(componentRef, context);
return (context: TContext) => renderComponent(componentRef, context, additionalProps);
}
unreachable(input);
@ -168,12 +171,14 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
* Create an event handler for a render prop
* @param renderInputValue the value of the render `@Input` property.
* @param jsxRenderer an optional renderer to use.
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
*/
protected createRenderPropHandler<TProps extends object>(
renderInputValue: InputRendererOptions<TProps>,
jsxRenderer?: JsxRenderFunc<TProps>
jsxRenderer?: JsxRenderFunc<TProps>,
additionalProps?: ExternalReactContentProps
): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null {
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue);
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue, additionalProps);
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
if (!renderInputValue) {

Просмотреть файл

@ -4,9 +4,10 @@ import * as ReactDOM from 'react-dom';
const DEBUG = false;
export const CHILDREN_TO_APPEND_PROP = 'children-to-append'; // TODO: Change to Symbol('children-to-append') after upgrade to TS 2.7.
export interface ReactContentProps {
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
/**
* Props that can be passed to `ReactContent` from users.
*/
export interface ExternalReactContentProps {
/**
* Experimental rendering mode.
* Uses a similar approach to `router-outlet`, where the child elements are added to the parent, instead of this node, and this is hidden.
@ -15,6 +16,12 @@ export interface ReactContentProps {
legacyRenderMode?: boolean;
}
// NOTE: Separated into `ExternalReactContentProps` since We only want a subset of props to be exposed to external users.
// With Omit type (TS 2.8) we can simply have `type ExternalReactContentProps = Omit<ReactContentProps, 'children-to-append'>`
export interface ReactContentProps extends ExternalReactContentProps {
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
}
/**
* Render any `HTMLElement`s (including Angular components) as a child of React components.
* Supports two rendering modes:
@ -23,11 +30,15 @@ export interface ReactContentProps {
* (similar to how `router-outlet` behaves in Angular).
*/
export class ReactContent extends React.PureComponent<ReactContentProps> {
componentDidMount() {
const element = ReactDOM.findDOMNode(this);
if (this.props[CHILDREN_TO_APPEND_PROP]) {
if (DEBUG) { console.warn('ReactContent Component > componentDidMount > childrenToAppend:', this.props[CHILDREN_TO_APPEND_PROP]); }
if (DEBUG) {
console.warn(
'ReactContent Component > componentDidMount > childrenToAppend:',
this.props[CHILDREN_TO_APPEND_PROP]
);
}
const hostElement = this.props.legacyRenderMode ? element : element.parentElement;
this.props[CHILDREN_TO_APPEND_PROP].forEach(child => hostElement.appendChild(child));

Просмотреть файл

@ -1,15 +1,13 @@
import { ComponentRef, TemplateRef } from "@angular/core";
import { ComponentRef, TemplateRef } from '@angular/core';
import * as React from 'react';
import { CHILDREN_TO_APPEND_PROP, ReactContent } from "../renderer/react-content";
import { CHILDREN_TO_APPEND_PROP, ReactContent, ExternalReactContentProps } from '../renderer/react-content';
function renderReactContent(rootNodes: HTMLElement[]): JSX.Element {
return React.createElement(
ReactContent,
{
[CHILDREN_TO_APPEND_PROP]: rootNodes
} as any
);
function renderReactContent(rootNodes: HTMLElement[], additionalProps?: ExternalReactContentProps): JSX.Element {
return React.createElement(ReactContent, {
...additionalProps,
[CHILDREN_TO_APPEND_PROP]: rootNodes,
});
}
/**
@ -17,12 +15,17 @@ function renderReactContent(rootNodes: HTMLElement[]): JSX.Element {
*
* @param templateRef The template to wrap
* @param context The context to pass to the template
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
*/
export function renderTemplate<TContext extends object>(templateRef: TemplateRef<TContext>, context?: TContext): JSX.Element {
export function renderTemplate<TContext extends object>(
templateRef: TemplateRef<TContext>,
context?: TContext,
additionalProps?: ExternalReactContentProps
): JSX.Element {
const viewRef = templateRef.createEmbeddedView(context);
viewRef.detectChanges();
return renderReactContent(viewRef.rootNodes);
return renderReactContent(viewRef.rootNodes, additionalProps);
}
/**
@ -30,11 +33,16 @@ export function renderTemplate<TContext extends object>(templateRef: TemplateRef
*
* @param htmlRenderFunc The function to wrap
* @param context The context to pass to the function
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
*/
export function renderFunc<TContext extends object>(htmlRenderFunc: (context: TContext) => HTMLElement, context?: TContext): JSX.Element {
export function renderFunc<TContext extends object>(
htmlRenderFunc: (context: TContext) => HTMLElement,
context?: TContext,
additionalProps?: ExternalReactContentProps
): JSX.Element {
const rootHtmlElement = htmlRenderFunc(context);
return renderReactContent([rootHtmlElement]);
return renderReactContent([rootHtmlElement], additionalProps);
}
/**
@ -42,10 +50,15 @@ export function renderFunc<TContext extends object>(htmlRenderFunc: (context: TC
*
* @param htmlRenderFunc The component reference to wrap
* @param context The context to pass to the component as `@Input`
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
*/
export function renderComponent<TContext extends object>(componentRef: ComponentRef<TContext>, context?: TContext): JSX.Element {
export function renderComponent<TContext extends object>(
componentRef: ComponentRef<TContext>,
context?: TContext,
additionalProps?: ExternalReactContentProps
): JSX.Element {
Object.assign(componentRef.instance, context);
componentRef.changeDetectorRef.detectChanges();
return renderReactContent([componentRef.location.nativeElement]);
return renderReactContent([componentRef.location.nativeElement], additionalProps);
}