NativeScript组件
一个模块,可轻松创建无需Angular的可重用NativeScript组件。
npm i --save nativescript-component

NativeScript组件

一种简单的方法来创建无需Angular的可重用NativeScript组件。

优点

  • 简单的API 用于定义组件并将其属性绑定到它。
  • 单个组件 的多个实例可以用于一个页面。
  • 每个组件实例都会自动获得其自己的 独立状态
  • 自动绑定 XML属性到组件的绑定上下文。
  • 父组件可以安全地将依赖项传递给其子组件,因为外部组件在嵌套组件之前初始化。
  • 自动绑定传递给组件视图的上下文属性 navigate()showModal()
  • 默认情况下,组件实例将在其视图的 unloaded 事件上自动 销毁
  • 可以将组件定义为 单例,以便在整个应用程序的生命周期内保持单个实例。

安装

npm install nativescript-component --save

示例

示例 1:sample-groceries应用

此示例 将从 NativeScript入门指南 中的标准sample-groceries应用更新为使用ES 6和nativescript-component进行基于组件的设计。为了参考,您可以将其与 原始版本 进行比较。

示例 2:嵌套组件

在此示例中,我们将创建一个名为 details-page 的父组件,该组件使用名为 editable-text 的另一个组件的多个实例,允许用户查看和编辑数据记录。

目录结构

app
|
|-- components
|
|-- details-page
| |
| |-- details-page.xml
| |-- details-page.js
|
|-- editable-text
|
|-- editable-text.xml
|-- editable-text.js

出于简洁起见,此示例中省略了样式,但将组件的样式分组在其目录中(例如 details-page/details-page.csseditable-text/editable-text.css)是良好的实践。查看Nativescript的LESSSASS 预编译插件以获取干净的样式。

details-page.xml

details-page 组件的模板由一个 ActionBar 组成,该 ActionBar 只有一个控件:一个按钮,用于在“视图”模式和“编辑”模式之间切换UI;以及一个列出我们的字段(姓名和姓氏)的 GridLayout

<Page navigatingTo="onNavigatingTo" xmlns:e="components/editable-text">

<Page.actionBar>
<ActionBar title="User Details">
<ActionItem text="Edit" ios.position="right" tap="edit" visibility="{{ controls.edit, controls.edit ? 'collapsed' : 'visible' }}"/>
<ActionItem text="Save" ios.position="right" tap="save" visibility="{{ controls.edit, controls.edit ? 'visible' : 'collapsed' }}"/>
</ActionBar>
</Page.actionBar>

<StackLayout>
<GridLayout columns="*,*" rows="auto,auto">
<Label text="First Name" col="0" row="0"/>
<e:editable-text class="name" record="{{ user }}" fieldName="firstName" controls="{{ controls }}" col="1" row="0"/>

<Label text="Last Name" col="0" row="1"/>
<e:editable-text class="name" record="{{ user }}" fieldName="lastName" controls="{{ controls }}" col="1" row="1"/>
</GridLayout>
</StackLayout>
</Page>
注意事项
  • 属性 navigatingTo="onNavigatingTo" 将组件的内置 onNavigatingTo() 钩子连接起来,该钩子在视图加载时实例化组件。

  • 属性 xmlns:e="components/editable-text" 定义了一个名为 "e" 的命名空间,以便我们的组件可以在XML中以 <e:editable-text/> 的形式引用。

  • 在属性 visibility="{{ controls.edit, controls.edit ? 'collapsed' : 'visible' }}" 中,将 controls.edit 传递为 {{ }} 的第一个参数,以便指示NativeScript,嵌套的 edit 属性应该是观察表达式后的数据源,而不是包含它的 controls 对象。此要求在 NativeScript数据绑定文档 中进行了记录。

details-page.js

对于此示例,假设另一个页面通过调用 navigate() 来导航到我们的 details-page 组件。

frames.topmost().navigate({
moduleName: 'components/details-page/details-page',
context: {
user: new Observable({ firstName: 'Brendan', lastName: 'Eich' })
}
});

《details-page》组件的JavaScript文件将看起来像这样

import { Observable } from 'data/observable';
import Component from 'nativescript-component';

class DetailsPage extends Component {

/**
* Place initialization code in `init`, which is automatically called
* after the parent is initialized and before child components are initialized.
*
* @override
*/

init() {
this.set('controls', new Observable({ edit: false }));
}

/**
* Switches the UI from view mode to edit mode.
*/

edit() {
/** @todo: Check if `this.set('controls.edit', true)` correctly sets the nested proeprty and, if not, implement support for that. */
let options = this.get('controls');
options.set('edit', true);
}

/**
* Switches the UI from edit mode to view mode.
*/

save() {
let options = this.get('controls');
options.set('edit', false);
}
}

DetailsPage.export(exports);
注意事项
  • 在组件的父组件(如果有)初始化后,会自动调用内置的init钩子。重写此钩子以使用内置方法和属性进行任何设置。this.get()this.set()用于在组件的绑定上下文中获取和设置属性。以这种方式设置的属性可以在组件的模板中显示。

  • 传递给navigate()的参数会自动绑定到组件的绑定上下文,这意味着在我们的示例中,user参数可以通过this.get('user')在JavaScript中访问,并在XML模板中作为{{ user }}可用。

  • export()用于以NativeScript运行时期望的格式导出类。

editable-text.xml

可以通过其父组件(《details-page》)在“查看”模式和“编辑”模式之间切换《editable-text》组件,因此其模板有一个只读的<Label/>,在“查看”模式下显示,以及一个可编辑的<TextField/>,在“编辑”模式下显示。

<StackLayout loaded="onLoaded">
<Label id="label" visibility="{{ controls.edit, controls.edit ? 'collapsed' : 'visible' }}"/>
<TextField id="input" visibility="{{ controls.edit, controls.edit ? 'visible' : 'collapsed' }}"/>
</StackLayout>

editable-text.js

《editable-text》组件从其父组件接受三个参数

  • record - 包含我们想要查看和编辑的属性的数据记录对象
  • fieldName - 我们想要查看和编辑的record对象中属性的名称
  • controls - 包含属性的对象,允许父组件动态控制子组件。在这个例子中,我们只使用单个controls.edit属性来指示文本字段是否处于编辑模式。
import Component from 'nativescript-component';

class EditableText extends Component {

/**
* @override
*/

init() {
// Set up the two-way binding for the data record's specified property.
// This must be done in JavaScript, because NativeScript's XML binding expressions don't currently support dynamic
// property names.
let label = this.view.getViewById('label'),
input = this.view.getViewById('input'),
record = this.get('record'),
fieldName = this.get('fieldName');

label.bind({
sourceProperty: fieldName,
targetProperty: 'text',
twoWay: true
}, record);

input.bind({
sourceProperty: fieldName,
targetProperty: 'text',
twoWay: true
}, record);
}
}

EditableText.export(exports);
注意事项
  • 作为XML属性传递的参数会自动设置在组件的绑定上下文中,并可以使用this.get()访问。

  • 组件的视图可以通过this.view访问。

  • 通常不需要像在init中这样手动设置绑定,但在这个情况下需要这样做,以便允许fieldName属性动态指定我们感兴趣的属性名称。

有关更多信息,请参阅API文档

注意事项

ListView.itemTemplateRepeater.itemTemplate中嵌入组件

  • 由于nativescript-component允许每个列表项都有自己的独立状态,因此它很好地服务于这种场景。
  • 然而,正如在sample-groceries应用中所注明的,你必须注意,在这种情况下,列表项会自动设置为组件的绑定上下文,最重要的是,它是不可变的。
  • 这意味着在你列表项组件中,你不能使用this.set('foo', foo)来在绑定上下文中设置新属性以在模板中显示该属性;相反,你必须在该对象作为数组传递给ListViewitems属性之前设置该属性。
  • 你仍然可以使用this.foo = foo来设置组件的实例属性,但它们不会在组件的模板中可用。

贡献

找到一个问题或有一个功能的想法?请随时提交PR或打开一个问题。

构建

此模块使用ES 6实现并转换为ES 5以导出。要构建源代码

npm run build

还有一个git预提交钩子,在提交时自动构建,因为dist目录已提交。

代码检查

npm run lint