Nativescript&Formly:在天堂做的比赛。
#javascript #android #ios #nativescript

对于任何想要在阅读其余帖子之前立即尝试演示的人。

  1. Open Stackblitz Nativescript preview
  2. 下载nativescript预览应用程序:android ios
  3. 登录,扫描代码并享受

语境

表格总是很痛苦,尤其是当它们尺寸较大时。想一想在高度受监管的行业中,有很多步骤和几个复杂性。作为开发人员,我们一遍又一遍地编码不同的元素,并照顾所有依赖性。开发完成后,两个月过去了,需求会发生变化,而贫穷的开发人员需要访问代码基础,并(希望)重构以适应更改。形式越大,依赖性变得越复杂。依赖性变得越复杂,涉及的重构就越多。

此外,移动团队还需要与更改保持同步,这也可能导致进一步的分歧。

我们不要忘记,开发API层的团队。他们还需要能够通过验证等维护字段等。

如果有一种可以在所有团队中共享schema的方法怎么办? ðÖ反映数据的模式,包括验证,可以照顾依赖关系,可以跨平台,可以与后端开发人员共享。

输入阿里巴巴的Formily,它确实做了上述。使用JSON模式,可以生成表单,同时控制数据模型。

官方:非常简短的描述

几个月前,我正式遇到了。它照顾了状态管理。

它不提供自己的组件库,而是提供 bridges ,您可以通过它连接组件库或自己的组件集。

作为蛋糕上的额外樱桃也提供:

  1. 可以复制模式的拖放设计师
  2. Web DevTools扩展程序能够调试。
  3. 几个组件库的包装器(元素UI,Ant Design,Vant,Semi等)

由于它是不可知论的,所以这是与Nativecript一起使用的理想选择。

入门

使用Nativescript团队与Stackblitz一起完成的惊人预览环境,是时候开始黑客入侵了。 (更多信息可以在https://preview.nativescript.org/上找到)

对于来自Web开发的人,NativeScript利用V8引擎直接与本机平台进行交互。这意味着基于您的移动开发专业知识,您可以:

  1. 写JavaScript,就好像您不知道基础平台一样。在这里,您可以选择您的味道,无论是反应,Vue Svelte,Vanilla。我的选择:绝对Vue
  2. 使用NativeScript SDK编写JavaScript以访问本地元素
  3. 写出反映平台SDK的JavaScript并将其编码为母语(Java/kotlin/objc/swift) 为了更好地解释这一点,请看一下this Youtube video by Nathan Walker

配置

我遇到的第一个打ic是,NativeScript正式支持Vue2(有一种使用DOMiNATIVE使用{n}运行vue3的方法,但这是另一个不同的主题)
正式,已经为此而征服了,但是它使用了一个称为vue-demi的软件包。

这花了一段时间来整理和stackblitz工作,我必须使用npm vue-demi-switch 2 nativescript-vue,根据文档用于:
a)指定版本
b)指定Vue的别名。

还使用
安装了构图API

import Vue from 'nativescript-vue';
import VueCompositionAPI from '@vue/composition-api';
Vue.use(VueCompositionAPI);

在页面中的下一个我遵循了formily
的设置

import { createForm } from "@formily/core";
import { createSchemaField } from "@formily/vue";
const { SchemaField } = createSchemaField({
  components: {}
})

从我的理解中,createForm负责数据方面和重新vitivitiy,而createSchemaField则创建了一个支持一组桥接组件的VUE组件。它需要一个JSON模式,从中生成儿童组件。

复制了基本的JSON模式,给出了模板

<Form :form="form">
    <SchemaField :schema="schema" />
</Form>

....并崩溃了。

由于正式是一个仍针对网络的框架,因此很明显,在某个时候将有一个<div/><form/>。目前,使用两个polyfills
解决了这一点

Vue.registerElement('div', () => StackLayout);
Vue.registerElement('form', () => StackLayout);

一旦完成,崩溃就停止了ð。

但是,到目前为止,还没有可见的组件。

创建桥梁。

到目前为止,SchemaField没有注册组件。因此,现在该建造一些桥梁了。

正好为此提供了一个vue库。
这些桥梁由3个部分组成。

  1. 您的组件。在我的情况下,nativescript vue组件。
  2. 使用@formily/vueconnect函数能够在格式中桥接您的组件具有的属性,属性,事件和儿童。通常,您还会使用mapProps函数来在两侧之间映射。一个示例将是使用value,但是Nativescript Textfield组件采用了称为text的道具,因此我们映射了Props
  3. 我说3?

这是一个简单的例子:

import { connect, mapProps, h } from '@formily/vue';
import { defineComponent } from 'vue-demi';

let input = defineComponent({
  name: 'FormilyTextField',
  props: {},
  setup(customProps: any, { attrs, slots, listeners }) {
    return () => {
      return h(
        'TextField',
        {
          attrs,
          on: listeners,
        },
        slots
      );
    };
  },
});

const Input = connect(input);

在上面的示例中可以看到vue-demi用于定义组件,并且nativescript TextField组件被桥接起来。

目前,我已经构建了以下组件列表。 (没有像官方包装纸那样详尽无遗的。
使用NativeScript本地组件

  • textfield-可以通过调整一些道具
  • 来映射到Input Textarea Password Number
  • 开关
  • datepicker
  • timepicker
  • listPicker-可以映射到Select

为了测试JSON模式,组件已在Schemafield和LO上注册,并观察到Magic我们拥有JSON模式形式! ð

创建装饰师ð

在正式的情况下,在输入和装饰的组件之间存在明显的分配。强烈指示的基本组件是FormItem,它照顾了:

  1. 标签
  2. 任何描述
  3. 任何反馈(错误消息等)
  4. 任何工具提示

由于这在NativeScript中不存在,因此再次创建了初始版本。
这次,需要创建要桥接的组件。

<template>
  <StackLayout
    :style="wrapperStyle"
    :class="wrapperClass"
    :orientation="layout"
    verticalAlignment="top"
  >
    <GridLayout columns="*,40" rows="auto" class="w-full items-center">
      <Label
        :text="`${label}${asterisk && required ? '*' : ''}`"
        :style="labelStyle"
        verticalAlignment="center"
        class="text-lg font-semibold"
        :class="labelClass"
        :textWrap="labelWrap"
        v-if="label"
      />
      <Label
        v-if="tooltip"
        @tap="showTooltip"
        col="2"
        class="bg-gray-100 rounded-full w-7 h-7 text-center text-xl"
        text="ℹ"
        horizontalAlignment="right"
      />
    </GridLayout>
    <GridLayout rows="auto">
      <slot></slot>
      <!-- slot outlet -->
    </GridLayout>
    <Label v-if="feedbackText" :text="feedbackText" :class="feedbackClass" />
  </StackLayout>
</template>

<script lang="ts">
import { defineComponent } from "vue-demi";
import Vue from "nativescript-vue";
import BottomSheetView from "~/component/BottomSheet/BottomSheetView.vue";
import { OpenRootLayout } from "~/component/OpenRootLayout";
export default defineComponent({
  name: "FormItem",
  props: {
    required: {
      type: Boolean,
    },
    label: {
      type: String,
    },
    labelStyle: {},
    labelClass: {},
    labelWrap: {
      type: Boolean,
      default: false,
    },
    layout: {
      type: String,
      default: "vertical",
    },
    tooltip: {},
    wrapperStyle: {},
    wrapperClass: {},
    feedbackText: {},
    feedbackStatus: {
      type: String,
      enum: ["error", "success", "warning"],
    }, // error/success/warning
    asterisk: {
      type: Boolean,
    },
    gridSpan: {},
  },
  computed: {
    feedbackClass(): string {
      switch (this.feedbackStatus) {
        case "error":
          return "text-red-400";
        case "success":
          return "text-green-400";
        case "warning":
          return "text-yellow-400";
        default:
          return "text-gray-100";
      }
    },
  },
  methods: {
    showTooltip() {
      let tooltipText = this.tooltip;
      const view = new Vue({
        render: (h) =>
          h(BottomSheetView, { props: { label: "Information" } }, [
            h("Label", {
              attrs: { text: tooltipText, textWrap: true, row: 2 },
              class: "w-full text-lg mb-8 leading-tight",
            }),
          ]),
      }).$mount().nativeView;

      OpenRootLayout(view);
    },
  },
});
</script>

nativecript在此提供了Web开发人员使用的所有正常VUE功能。有一个微妙的差异:没有HTML属性。但是,在这方面,人们可以轻松地将知识从HTML转移到NativeScript。

  1. StackLayout-让您垂直或水平堆叠孩子
  2. GridLayout-是一个布局容器,可让您以桌子般的方式排列其子元素。网格由行,列和单元组成。单元可以跨越一个或多个行和一个或多个列。它可以包含多个子元素,这些元素可以跨越多行和列,甚至相互重叠。默认情况下,有一列和一行。
  3. Label-持有一些文字

风格的明智之举,因为人们可以轻松地看到正在使用尾风效用课程。
到目前为止,道具公开了必要的功能。

该组件目前有一种方法,即生成工具提示。在这方面,我们可以利用NativeScript提供的另一个布局容器:根布局

rootlayout

<RootLayout> is a layout container designed to be used as the primary root layout container for your app with a built in api to easily control dynamic view layers. It extends a GridLayout so has all the features of a grid but enhanced with additional apis.是文档给出的定义。
用更谦虚的话来说,想一想这种布局可以在其他所有内容上打开任何组件。这对于底部纸,像模态这样的组件,侧菜单非常好。让您的想象力在这里松动。

使其与NativeScript Vue一起工作。

  1. 我创建了一个vue组件
  2. 我安装了视图组件
  3. 我称之为辅助功能,该函数召唤了rootlayout,并告诉它打开此组件。 这个辅助功能无非是
getRootLayout().open(myComponent, ...some overlay settings like color and opacity, ...some animation setting)

长话短说:使用相同的组件用于datepicker
Tooltip using RootLayout
DatePicker using rootLayout

完成桥梁

在此阶段,我们有一个由JSON模式生成的函数形式。但是,数据尚未正确反映。

为什么?

原因很简单,正式希望在change事件中从组件中收回这些信息。
深入挖掘其元素UI包装器,他们使用VUE-DEMI转换任何组件,以便将Web输入函数映射到此更改事件。

一个问题。

vue不支持input事件或change事件。因此,根据组件事件将听众介绍给桥梁(例如:Textfield具有textChanged)。反过来,此组件特定事件又发出了一个合并事件input,其中包括组件的值。
这立即将完全反应性归还回到官方。

演示时间

在进行快速动画GIF演示之前。这是使用的演示JSON定义:

{
        type: "object",
        properties: {
          firstName: {
            type: "string",
            title: "Test2",
            required: true,
            "x-component": "Input",
            "x-component-props": {
              hint: "First Name",
            },
          },
          lastName: {
            type: "string",
            "x-component-props": {
              hint: "Last Name",
            },
            required: true,
            "x-component": "Input",
          },
          username: {
            type: "string",
            title: "Username",
            required: true,
            "x-decorator": "FormItem",
            "x-component": "Input",
            "x-component-props": {
              hint: "@ChuckNorris...",
              disabled: true,
            },
            "x-decorator-props": {
              tooltip: "Lorem ipsum test a tooltip sheet with some text here.",
            },
            "x-reactions": {
              dependencies: ["firstName", "lastName"],
              fulfill: {
                state: {
                  value:
                    "{{$deps[0] ? `@${$deps[0]}${($deps[1] ? '.' + $deps[1] : '')}` : undefined}}",
                },
              },
            },
          },
          password: {
            type: "string",
            title: "Password",
            required: true,
            "x-decorator": "FormItem",
            "x-component": "Password",
            pattern: '/^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/'
          },

          testSwitch: {
            type: "string",
            title: "Rememeber me?",
            required: true,

            "x-decorator": "FormItem",
            "x-component": "Switch",
          },
          from: {
            type: "string",
            title: "Appointment Date",
            required: true,
            "x-decorator": "FormItem",
            "x-component": "DatePicker",
          },
          time: {
            type: "string",
            title: "Appointment Time",
            required: true,
            "x-decorator": "FormItem",
            "x-component": "TimePicker",
          },
          country: {
            type: "string",
            title: "Country",
            required: true,
            "x-decorator": "FormItem",
            "x-component": "Select",
            enum: [{
              label: "?? Canada",
              value: "CA"
            },{
              label: "?? United Kingdom",
              value: "UK"
            },{
              label: "?? United States",
              value: "Us"
            }]
          },
        },
      },
    }

这是此模式上的一些指示:

  1. 最终数据对象将拥有的每个嵌套对象(例如firstNamelastName等)的关键。
  2. x-component指示要使用的组件
  3. x-decorator指示输入组件周围的装饰
  4. 一些基本验证,例如requiredpattern作为顶级。其中包括minimummaximum,但还可以包括自定义验证器,包括异步验证
  5. 一个字段的任何键都可以用{{}}写为JSX。这有可能在模式中包括一些逻辑。
  6. 在用户名段下发现的x-reactions会照顾两个依赖项:firstNamelastName,并通过根据依赖项调整值来实现反应。官方支持两种类型的反应
    • 活动:含义当前的活动组件,在另一个组件中更改某些内容
    • 反应性:一个组件会倾听到另一个组件的变化。
  7. 组件和装饰师可以分别使用x-component-propsx-decorator-props接收其他道具。

这是该应用程序的快速屏幕抓取:

Formily with NS

包装

可以在以下存储库中找到完整的代码,并且可以使用Stackblitz Preview和NativeScript Preview Application

轻松测试。

https://github.com/azriel46d/nativescript-formily-example