@acpaas-ui/embeddable-widgets 中文文档教程

发布于 4年前 浏览 23 项目主页 更新于 3年前

ACPaaS UI Embeddable Widgets

该库支持在运行时使用 iframe 将一个应用程序的一部分跨框架嵌入到另一个应用程序的页面中。

可以嵌入的应用程序部分称为小部件。 发布小部件的应用称为发布者。 将发布的小部件嵌入到页面中的应用程序称为容器。

小部件可以声明在容器页面中可用的 API,即使容器和发布者是在不同的前端框架中实现的。

Using

如果您要从该框架的 v1.x 迁移到 v2.x,请注意存在重大更改,请参阅迁移说明 以及 更新日志

Including

发布和嵌入的第一步是将小部件库包含到页面中。

<script src="https://cdn.antwerpen.be/aui_embeddable_widgets/2.0.0/aui-embeddable-widgets.min.js"></script>

如果你不想从 CDN 加载,你也可以 npm install @acpaas-ui/embeddable-widgets 你会在 node_modules/@acpaas-ui/embeddable 中找到库-widgets/lib 文件夹。

Publishing

您可以将任何网页发布为可嵌入的小部件。

Declaring

首先,您需要在 Web 可访问的 URL 上以 JSON 格式发布小部件的定义。

{
  "tag": "my-foo-widget",
  "url": "/foo-widget",
  "dimensions": {
    "width": "100%",
    "height": "500px"
  },
  "props": {
    "fooData": {
      "type": "array",
      "required": true
    },
    "onFoo": {
      "type": "function",
      "required": false
    }
  }
}

这是怎么回事:

  • The tag is a unique identifier for the widget in the page (it is not automatically mapped to a HTML tag).
  • The url points to where the widget's page is hosted. It can be relative (to the JSON's URL) or absolute.

不需要将定义托管在与小部件相同的服务器上。

  • The dimensions specify the initial rendering dimensions, applies as style attributes to the iframe.
  • The props specify the properties that the widget can be initialized with
  • fooData is an array which will be passed from container to widget
  • onFoo is an event handler which will be defined in the container and called by the widget

如果你想在同一个页面上多次渲染同一个组件,但具有不同的定义值。 您将必须 define() 具有不同标签的组件。

有关详细信息,请参阅下面的 API 部分。

需要在此 JSON 上设置 CORS 标头(例如 Access-Control-Allow-Origin: *)。 不需要在小部件页面本身上设置 CORS 标头。

Angular

要发布 Angular 6+ 应用程序,请使用 Angular 包装器 ngx-embeddable-widgets。 它包括一个示例应用程序。

Other Frameworks

初始化小部件:

window.auiEmbeddableWidgets.load('//example.com/path/to/definition.json');

访问从 window.xprops 中的容器传递的属性。

function doFoo() {
  if (window.xprops && window.xprops.fooData) {
    const result = window.xprops.fooData.map(...);
    if (window.xprops.onFoo) {
      window.xprops.onFoo(result);
    }
  }
}

小部件的页面不需要跨源标头。 它通过 window.postMessage 与容器通信。 但是,如有必要,它应该允许通过设置适当的 X-Frame-OptionsContent-Security-Policy 标头来构建框架。

Embedding

Angular

要嵌入 Angular 6+ 应用程序,请使用 Angular 包装器 ngx-embeddable-widgets

React

import React from 'react';
import ReactDOM from 'react-dom';

const MyWidget = window.auiEmbeddableWidgets.reactComponent(
  // url to the definition
  "//example.com/path/to/defintion.json",
  { React, ReactDOM }
)

class App extends Component {

  onFoo(result) { ... }

  render() {
    return (
      <MyWidget
        fooData={ ['one', 'two'] }
        onFoo={ result => this.onFoo(result) }
        className="my-widget"
        />
    );
  }
}

这将呈现一个

并应用了(可选)className

Other

Provide a div to render the widget in:

<div id="my-container"></div>

将小部件渲染到 div 中,将必要的属性传递给它:

window.auiEmbeddableWidgets.renderUrl(
  '//example.com/path/to/definition.json',
  { fooData: ['one', 'two'],
    onFoo: function(result) { ... } },
  document.getElementById('my-container')
);

Migrating

如果您当前在应用程序或小部件中使用此库的 v1.x,则必须注意正确升级。 应用程序和小部件必须运行相同的可嵌入小部件库的主要版本。

此库将 auiapi_version 查询参数附加到它加载到 iframe 中的 URL。 基于此,应在小部件页面内加载适当的库版本。 v1.x 用于 _aui_api_version=1v2.x 用于 _aui_api_version=2

要在小部件的应用程序中加载此库的多个版本,您可以使用 npm 功能来拥有同一库的多个版本

如果 widget 和 app 分开托管,建议的升级

  1. Upgrade the widget to interpret _aui_api_version and load the appropriate library version.
  2. Upgrade the app to use the new major version of the library.

API

window.auiEmbeddableWidgets

  • 策略 覆盖并返回一个组合对象,其中包含实例化组件所需的一切。 对象具有:

  • 选项:具有处理过的默认值的定义

  • 覆盖:您传递下来的覆盖

  • 组件:将属性传递给并实例化的函数

每个小部件都有一个唯一的标签。 每个标签在页面中只能定义一次,但可以渲染多次。 但是,如果您想更改同一个小部件的尺寸,则必须使用新标签重新定义一个。

  • isDefined(tag: string): boolean

    如果小部件已在页面中定义,则返回 true。

  • load(url: string, ?overrides: object): Promise

    从 URL 加载小部件定义,将可选覆盖应用于加载的定义,然后返回小部件的句柄以进行实例化。

  • render(tag: string|object, props: object, elem: HTMLElement): object

    使用指定的 props 参数将先前定义的小部件渲染到指定的元素中,并返回实例的句柄。 tag 可以是从加载操作返回的小部件实例,或其标记字符串。

  • renderUrl(url: string, props: object, elem: HTMLElement, ?overrides: object): Promise

    从 URL 加载小部件定义(如果尚未加载),将可选的覆盖应用于加载定义,然后使用给定的 props 参数将其渲染到指定的元素。 返回呈现实例的承诺。

  • reactComponent(url: string, deps: object, ?overrides: object): object

    为小部件创建一个 React 组件,其定义托管在 url 中,并应用可选的覆盖那个定义。 deps 对象必须包含 React 提供的 ReactReactDOM 对象。

Definition attributes

小部件定义的可能属性:

tag string [required]

组件的标记名称,用于:

  • Loading the correct component in the child window or frame
  • Generating framework drivers
  • Logging
tag: 'my-component-tag'

url string [required]

呈现小部件时将加载的 URL。 可以相对于 JSON 的 URL 或绝对。

url: 'https://example.com/foo-widget'
url: '/foo-widget'

dimensions { width : string, height : string }

小部件的初始尺寸,以 css 样式为单位,支持 px%

dimensions: {
    width: '300px',
    height: '200px'
}
dimensions: {
    width: '80%',
    height: '90%'
}

尺寸是在 define() 时设置的,而不是在 render() 时设置的。 要从默认值覆盖尺寸,可以将它们作为 overrides 参数的属性传递给 renderUrl。 但是,这将在连续调用同一小部件​​的 renderUrl() 时被忽略。 要使用不同的尺寸多次呈现相同的小部件,建议使用宽度和高度 100%,并将每个小部件放在适当大小的容器 div 中。

props Object<string, Object>

渲染时可以传递给小部件的道具(数据或函数)。

props: {

    onLogin: {
        type: 'function'
    },

    prefilledEmail: {
        type: 'string',
        required: false
    }
}
Default props
scrollTo(yPos: Numer, tag: String)

有时您需要将页面滚动到 iframe 外部以匹配 iframe 内部的元素。 为此,可以从小部件内部调用 scrollTo 属性:

this.props.scrollTo(elementOffset, this.props.tag);

库提供了 scrollTo 的默认处理程序。 您可以通过将自己的实现作为 props.scrollTo 来覆盖它,例如,以补偿​​标题元素。 这是默认实现:

const scrollTo = (elementOffset, tag) => {
  const containerElement = document.querySelector(`[id^='zoid-${tag}-']`);
  const newTopOffset = containerElement.offsetParent.offsetTop + elementOffset;
  window.scrollTo({
    top: newTopOffset,
    behavior: 'smooth',
  });
};

注意:window.scrollTo 由该库填充。

Prop Options
  • type string

    prop

  • 'string'

  • 'number'

  • 'boolean' 期望的数据类型

  • '对象'

  • '函数'

  • '数组'

  • 必需 布尔值

    该道具是否是强制性的。 默认为 true

  onLogin: {
      type: 'function',
      required: false
  }
  • defaultValue

    prop 的默认值(如果在渲染时未传递)。 required 必须为假。

  fooData: {
      type: "array",
      required: false,
      defaultValue: ["one", "two"]
  }

这可以是任何类型的值。 但是,如果您传递一个函数,它将以 props 作为第一个参数被调用。 因此,如果您想要一个函数作为 defaultValue,请确保将其包装起来。

  • 查询参数 boolean | string

    是否应该在 url 中传递一个 prop(这样它可以影响路由)?

  email: {
      type: 'string',
      queryParam: true // ?email=foo@bar.com
  }

如果设置了字符串,则指定将使用的 url 参数名称。

  email: {
      type: 'string',
      queryParam: 'user-email' // ?user-email=foo@bar.com
  }
  • serialization string

    如果是 json,prop 将在插入到 url 之前被 JSON stringified

  user: {
      type: 'object',
      serialization: 'json' // ?user={"name":"Zippy","age":34}
  }

如果是 dotify,prop 将转换为点符号。

  user: {
      type: 'object',
      serialization: 'dotify' // ?user.name=Zippy&user.age=34
  }

如果是 base64,prop 将被 JSON 字符串化,然后在插入 url 之前进行 base64 编码

  user: {
      type: 'object',
      serialization: 'base64' // ?user=eyJuYW1lIjoiWmlwcHkiLCJhZ2UiOjM0fQ==
  }

autoResize { height: boolean, width: boolean, element: string }

当子窗口小部件窗口大小更改时,使容器的 iframe 自动调整大小。

autoResize: {
    width: false,
    height: true,
}

请注意,默认情况下它与内容的 body 元素相匹配。 您可以通过将自定义选择器指定为 element 属性来覆盖此设置。

autoResize: {
    width: false,
    height: true,
    element: '.my-selector',
}

建议仅对高度使用 autoResize。 宽度有一些奇怪的效果,尤其是当存在滚动条时。

defaultLogLevel string

小部件内部的默认日志记录级别,有助于调试。 选项是:

  • 'debug'
  • 'info'
  • 'warn' (default)
  • 'error'
defaultLogLevel: 'info'

请注意,可以通过在呈现组件时将 logLevel 作为道具传递来覆盖此值。

Additional properties

查看 Zoid API 文档了解更多属性。 基于函数的属性只能指定为覆盖,不能在 JSON 中指定。

Developing

  • Run a server publishing the embeddable widgets framework
  npm install
  npm start
  • Point your publisher and container apps to this locally hosted version.

有关更多详细信息,请参阅贡献指南

Design notes

该框架利用 Zoid 框架,它实现了使用 iframe 和 postMessage API 将应用程序嵌入其他应用程序的样板。

包装器是必要的,以提供更适合 Digipolis 开发项目需求的不同开发人员体验。

  • Zoid loads widget definitions synchronously from a script tag. This framework loads them asynchronously from JSON
  • no foreign code needs to execute inside the container app, low-risk
  • changes to the JSON's schema are easy to support with framework upgrades
  • no globals aside from the widgets framework itself
  • Zoid requires the widget to know its own URL's, this doesn't
  • The widget does not need to known its own absolute URL, because the JSON can have a relative url
  • The widget framework uses the absolute URL of the JSON passed to renderUrl to determine the URL for the widget page itself
  • Zoid supports popup windows, this doesn't
  • It adds a lot of code, and it still is very tricky in IE (see zoid documentation)
  • All zoid API's are wrapped to allow replacing zoid later on and to support additional logic
  • defaultLogLevel = warn, whereas zoid has defaultLogLevel = info (which is spammy)
  • Can still be overridden by the widget's JSON
  • The framework itself is purely client-side, to allow hosting on a CDN.

Contributing

始终欢迎拉取请求,但请记住以下事项:

  • New features (both breaking and non-breaking) should always be discussed with the repo's owner. If possible, please open an issue first to discuss what you would like to change.
  • Fork this repo and issue your fix or new feature via a pull request.
  • Please make sure to update tests as appropriate. Also check possible linting errors and update the CHANGELOG if applicable.

Support

Joeri Sebrechts (joeri.sebrechts@digipolis.be)

License

MIT

版权所有 (c) 2019 年至今,Digipolis

ACPaaS UI Embeddable Widgets

This library enables cross-framework embedding of a part of one application into a page of another application at run-time using iframes.

An application part that can be embedded is called a widget. The app which publishes the widget is called the publisher. The app which embeds the published widget into a page is called the container.

Widgets can declare API's that are available in the container's page, even if container and publisher are implemented in different front-end frameworks.

Using

If you are migrating from v1.x of this framework to v2.x be aware that there are breaking changes, see the migration notes as well as the changelog.

Including

A first step for both publishing and embedding is including the widgets library into the page.

<script src="https://cdn.antwerpen.be/aui_embeddable_widgets/2.0.0/aui-embeddable-widgets.min.js"></script>

If you don't want to load from CDN, you can also npm install @acpaas-ui/embeddable-widgets and you will find the library in the node_modules/@acpaas-ui/embeddable-widgets/lib folder.

Publishing

You can publish any webpage as an embeddable widget.

Declaring

First you need to publish the definition of the widget on a web-accessible URL as JSON.

{
  "tag": "my-foo-widget",
  "url": "/foo-widget",
  "dimensions": {
    "width": "100%",
    "height": "500px"
  },
  "props": {
    "fooData": {
      "type": "array",
      "required": true
    },
    "onFoo": {
      "type": "function",
      "required": false
    }
  }
}

What is going on here:

  • The tag is a unique identifier for the widget in the page (it is not automatically mapped to a HTML tag).
  • The url points to where the widget's page is hosted. It can be relative (to the JSON's URL) or absolute.

It is not required that the definition is hosted on the same server as the widget.

  • The dimensions specify the initial rendering dimensions, applies as style attributes to the iframe.
  • The props specify the properties that the widget can be initialized with
  • fooData is an array which will be passed from container to widget
  • onFoo is an event handler which will be defined in the container and called by the widget

If you want to render the same component multiple times on the same page but with different definition values. You will have to define() the component with different tags.

See the API section below for more details.

CORS headers need to be set on this JSON (e.g. Access-Control-Allow-Origin: *). CORS headers do not need to be set on the widget page itself.

Angular

To publish an Angular 6+ app, use the Angular wrapper ngx-embeddable-widgets. It includes an example app.

Other Frameworks

Initialize the widget:

window.auiEmbeddableWidgets.load('//example.com/path/to/definition.json');

Access the properties passed from the container in window.xprops.

function doFoo() {
  if (window.xprops && window.xprops.fooData) {
    const result = window.xprops.fooData.map(...);
    if (window.xprops.onFoo) {
      window.xprops.onFoo(result);
    }
  }
}

The widget's page does not need cross-origin headers. It communicates to the container via window.postMessage. However, it should allow framing by setting appropriate X-Frame-Options or Content-Security-Policy headers if necessary.

Embedding

Angular

To embed into an Angular 6+ app, use the Angular wrapper ngx-embeddable-widgets.

React

import React from 'react';
import ReactDOM from 'react-dom';

const MyWidget = window.auiEmbeddableWidgets.reactComponent(
  // url to the definition
  "//example.com/path/to/defintion.json",
  { React, ReactDOM }
)

class App extends Component {

  onFoo(result) { ... }

  render() {
    return (
      <MyWidget
        fooData={ ['one', 'two'] }
        onFoo={ result => this.onFoo(result) }
        className="my-widget"
        />
    );
  }
}

This renders a <div> with the (optional) className applied to it.

Other

Provide a div to render the widget in:

<div id="my-container"></div>

Render the widget into the div, passing it the necessary properties:

window.auiEmbeddableWidgets.renderUrl(
  '//example.com/path/to/definition.json',
  { fooData: ['one', 'two'],
    onFoo: function(result) { ... } },
  document.getElementById('my-container')
);

Migrating

If you're currently using v1.x of this library in an app or in a widget, care must be taken to upgrade properly. Both app and widget must be running the same major version of the embeddable widgets library.

This library appends the auiapi_version query parameter to the URL it loads into the iframe. Based on this the appropriate library version should be loaded inside of the widget's page. v1.x for _aui_api_version=1, and v2.x for _aui_api_version=2.

To load multiple versions of this library inside the widget's app, you can use the npm feature to have multiple versions of the same library.

A suggested upgrade strategy in case widget and app are separately hosted:

  1. Upgrade the widget to interpret _aui_api_version and load the appropriate library version.
  2. Upgrade the app to use the new major version of the library.

API

window.auiEmbeddableWidgets

  • define(definition: object, ?overrides: object): object

    Defines a widget from the specified definition (same as the JSON described above) and definition overrides and returns a composed object with everything required to instantiate a component. Object has:

  • options: the definition with processed default values

  • overrides: the overrides you passed down

  • component: a function to pass the properties to and instantiate

Each widget has a unique tag. Each tag can only be defined once in the page, but can be rendered multiple times. However if you want to change the dimensions on the same widget, you will have to redefine one with a new tag.

  • isDefined(tag: string): boolean

    Returns true if the widget is already defined in the page.

  • load(url: string, ?overrides: object): Promise<object>

    Loads a widget definition from a URL, applies the optional overrides to the loaded definition, then returns a handle to the widget for instantiating.

  • render(tag: string|object, props: object, elem: HTMLElement): object

    Renders a previously defined widget with the specified props parameters into the specified element and returns a handle to the instance. tag can be the widget instance returned from the load operation, or its tag string.

  • renderUrl(url: string, props: object, elem: HTMLElement, ?overrides: object): Promise<object>

    Loads a widget definition from URL (if not yet loaded), applies the optional overrides to the loaded definition, then renders it to the specified element with the given props parameters. Returns a promise for the rendered instance.

  • reactComponent(url: string, deps: object, ?overrides: object): object

    Creates a React component for the widget with definition hosted at url, with the optional overrides applied to that definition. The deps object must contain the React and ReactDOM objects provided by React.

Definition attributes

The possible attributes for the widget definition:

tag string [required]

A tag-name for the component, used for:

  • Loading the correct component in the child window or frame
  • Generating framework drivers
  • Logging
tag: 'my-component-tag'

url string [required]

The URL that will be loaded when the widget is rendered. Can be relative to the JSON's URL or absolute.

url: 'https://example.com/foo-widget'
url: '/foo-widget'

dimensions { width : string, height : string }

The initial dimensions for the widget, in css-style units, with support for px or %.

dimensions: {
    width: '300px',
    height: '200px'
}
dimensions: {
    width: '80%',
    height: '90%'
}

Dimensions are set at define() time, not at render() time. To override dimensions from the defaults they can be passed as a property of the overrides argument to renderUrl. However, this will be ignored on successive calls to renderUrl() for the same widget. To render the same widget multiple times with different dimensions it is suggested to use width and height 100% and put each widget in a container div that is appropriately sized.

props Object<string, Object>

Props that can be passed to the widget when rendering (data or functions).

props: {

    onLogin: {
        type: 'function'
    },

    prefilledEmail: {
        type: 'string',
        required: false
    }
}
Default props
scrollTo(yPos: Numer, tag: String)

Sometimes you need to scroll the page outside the iframe to match an element inside the iframe. In order to do this, from inside the widget the scrollTo prop can be called:

this.props.scrollTo(elementOffset, this.props.tag);

There is a default handler for scrollTo that is provided by the library. You can override it by passing your own implementation as props.scrollTo, for example, to compensate for header elements. This is the default implementation:

const scrollTo = (elementOffset, tag) => {
  const containerElement = document.querySelector(`[id^='zoid-${tag}-']`);
  const newTopOffset = containerElement.offsetParent.offsetTop + elementOffset;
  window.scrollTo({
    top: newTopOffset,
    behavior: 'smooth',
  });
};

NOTE: window.scrollTo is polyfilled by this library.

Prop Options
  • type string

    The data-type expected for the prop

  • 'string'

  • 'number'

  • 'boolean'

  • 'object'

  • 'function'

  • 'array'

  • required boolean

    Whether or not the prop is mandatory. Defaults to true.

  onLogin: {
      type: 'function',
      required: false
  }
  • defaultValue

    The default value for the prop if not passed at render time. required must be false.

  fooData: {
      type: "array",
      required: false,
      defaultValue: ["one", "two"]
  }

This can be any type of value. However if you pass a function, it will be called with props as the first argument. So if you want to have a function as defaultValue, make sure you wrap it.

  • queryParam boolean | string

    Should a prop be passed in the url (so it can influence the routing)?

  email: {
      type: 'string',
      queryParam: true // ?email=foo@bar.com
  }

If a string is set, this specifies the url param name which will be used.

  email: {
      type: 'string',
      queryParam: 'user-email' // ?user-email=foo@bar.com
  }
  • serialization string

    If json, the prop will be JSON stringified before being inserted into the url

  user: {
      type: 'object',
      serialization: 'json' // ?user={"name":"Zippy","age":34}
  }

If dotify the prop will be converted to dot-notation.

  user: {
      type: 'object',
      serialization: 'dotify' // ?user.name=Zippy&user.age=34
  }

If base64, the prop will be JSON stringified then base64 encoded before being inserted into the url

  user: {
      type: 'object',
      serialization: 'base64' // ?user=eyJuYW1lIjoiWmlwcHkiLCJhZ2UiOjM0fQ==
  }

autoResize { height: boolean, width: boolean, element: string }

Makes the container's iframe resize automatically when the child widget window size changes.

autoResize: {
    width: false,
    height: true,
}

Note that by default it matches the body element of your content. You can override this setting by specifying a custom selector as an element property.

autoResize: {
    width: false,
    height: true,
    element: '.my-selector',
}

Recommended to only use autoResize for height. Width has some strange effects, especially when scroll bars are present.

defaultLogLevel string

The default logging level for the widget's internals, helpful for debugging. Options are:

  • 'debug'
  • 'info'
  • 'warn' (default)
  • 'error'
defaultLogLevel: 'info'

Note that this value can be overriden by passing logLevel as a prop when rendering the component.

Additional properties

Check the Zoid API documentation for additional properties. The function-based properties can only be specified as overrides, not in the JSON.

Developing

  • Run a server publishing the embeddable widgets framework
  npm install
  npm start
  • Point your publisher and container apps to this locally hosted version.

See the contribution guide for additional details.

Design notes

This framework makes use of the Zoid framework, which implements the boilerplate for embedding apps into other apps using iframes and the postMessage API.

The wrapper is necessary to allow for a different developer experience which is more suited to the needs of Digipolis development projects.

  • Zoid loads widget definitions synchronously from a script tag. This framework loads them asynchronously from JSON
  • no foreign code needs to execute inside the container app, low-risk
  • changes to the JSON's schema are easy to support with framework upgrades
  • no globals aside from the widgets framework itself
  • Zoid requires the widget to know its own URL's, this doesn't
  • The widget does not need to known its own absolute URL, because the JSON can have a relative url
  • The widget framework uses the absolute URL of the JSON passed to renderUrl to determine the URL for the widget page itself
  • Zoid supports popup windows, this doesn't
  • It adds a lot of code, and it still is very tricky in IE (see zoid documentation)
  • All zoid API's are wrapped to allow replacing zoid later on and to support additional logic
  • defaultLogLevel = warn, whereas zoid has defaultLogLevel = info (which is spammy)
  • Can still be overridden by the widget's JSON
  • The framework itself is purely client-side, to allow hosting on a CDN.

Contributing

Pull requests are always welcome, however keep the following things in mind:

  • New features (both breaking and non-breaking) should always be discussed with the repo's owner. If possible, please open an issue first to discuss what you would like to change.
  • Fork this repo and issue your fix or new feature via a pull request.
  • Please make sure to update tests as appropriate. Also check possible linting errors and update the CHANGELOG if applicable.

Support

Joeri Sebrechts (joeri.sebrechts@digipolis.be)

License

MIT

Copyright (c) 2019-present, Digipolis

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文