@360works/fmpromise 中文文档教程

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

fmPromise: a Richer JavaScript Web Viewer Integration

FileMaker 19 添加了从 JavaScript 调用 FileMaker 脚本以及从 FileMaker 脚本执行 JavaScript 函数的功能。 这允许 FileMaker 和 JavaScript/WebViewer 之间的集成,但有一些我们可以改进的笨拙部分。

  • Every call from JavaScript to a FileMaker script needs a public JavaScript function to receive the script response, and it is the responsibility of the Script to call that function.
  • All data coming back from FileMaker is a string
  • Debugging JavaScript errors is very difficult without browser-based dev tools
  • The window.FileMaker object is not available right when the page loads, so you need a window.setTimeout() to wait for it to become available if you want to populate your web viewer using a script call.

fmPromise 旨在解决这些缺点,并帮助您以最少的麻烦在您的解决方案中使用网络查看器。

长话短说,而不是像这样的 JavaScript:

function submitMyOrder(orderDetails) {
    window.progressDialog = showProgressDialog('Submitting...'); // global scope, not ideal
    const scriptParam = JSON.stringify(orderDetails); // convert JS object to string
    try {
        window.FileMaker.PerformScript('Submit Order from WebViewer', scriptParam); // no return value
    } catch (e) {
        showError('Could not call script "Submit Order from WebViewer", was it renamed?" ' + e);
    }
}

// the FileMaker `Submit Order from WebViewer` script is responsible for calling this on success 
function submitOrderSuccessCallback(payloadString) {
    const submitResult = JSON.parse(payloadString); // convert string to JS objects
    showSubmitResult(submitResult);
    window.progressDialog.close();
}

// the FileMaker `Submit Order from WebViewer` script is responsible for calling this on failure 
function submitOrderErrorCallback(msg) {
    showError('Could not send order: ' + msg);
    window.progressDialog.close();
}

使用 fmPromise,调用 fmPromise.performScript() 返回一个 Promise 完成错误处理。 这让您可以像这样编写 JavaScript:

// WITH fmPromise, the call to performScript returns a Promise
// the `ScriptResult` of `Find Matching Contacts` will be parsed as JSON and used to resolve the Promise
function submitMyOrder(orderDetails) {
    const progressDialog = showProgressDialog('Submitting...'); // block scope
    fmPromise.performScript('Submit Order from WebViewer', orderDetails) // returns a Promise
        .then(function (submitResult) {
            showSubmitResult(submitResult);
        })
        .catch(function (error) {
            showError('Could not send order: ' + error);
        }).finally(function () {
        progressDialog.close();
    })
}

添加 async/await< 的语法糖/a> 并且您可以拥有这个:

async function submitMyOrder(orderDetails) {
    const progressDialog = showProgressDialog('Submitting...');
    try {
        const submitResult = await fmPromise.performScript('Submit Order from WebViewer', orderDetails);
        showSubmitResult(submitResult);
    } catch (error) {
        showError('Could not send order: ' + msg);
    } finally {
        progressDialog.close();
    }
}

Debugging

强烈建议您在您的网络查看器中启用外部 JavaScript 调试,如 此处

在您的终端中,键入:

defaults write com.FileMaker.client.pro12 WebKitDebugDeveloperExtrasEnabled -bool YES

这允许您在您的网络查看器代码上使用 Safari 的开发人员工具,它是非常有用。

Packaging

FMPromise Add-On 工作流程是:

  1. Create a new module, which writes my-module.html to your Documents/fmPromise/ directory.
  2. Edit this file and preview it in $$FMPROMISE_DEVMODE
  3. Once satisfied, package the module into the fmPromiseModule table

此打包步骤获取您的 .html 文件的源,并可选择内联任何外部 JavaScript / CSS 文件。

如果您想更改脚本或样式的内联行为,请将 data-package 属性添加到您的

  • data-package="omit" will remove the tag entirely. This is handy for things which you only want present in dev mode, like Vue Dev Tools.
  • data-package="leave" will not inline the file, but it will remaing as an external resource. This is good for large external libraries, but means your module will probably not work without internet access.

API

fmPromise.performScript(scriptName, parameter, option) 执行 FileMaker 脚本,返回 Promise。 Promise 将通过脚本结果解析(如果可能,解析为 JSON),或者如果 FileMaker 脚本结果以单词“ERROR”开头则被拒绝。 第三个 option 参数指定如何处理已在运行的 FileMaker 脚本。 请参阅 FileMaker 9.1.2 发行说明了解细节。

fmPromise.evaluate(expression, letVars) 使用可选的 letVars 计算 FileMaker 中的表达式。 这也是设置 $$GLOBAL 变量的便捷方式。

fmPromise.insertFromUrl(url, curlOptions) 从 URL 插入,无需担心对 Web 查看器施加的跨站点脚本限制。

fmPromise.setFieldByName(url, curlOptions) 在 FileMaker 中按名称设置字段。

fmPromise.executeSql(sql, ...bindings) 执行带占位符的 SQL 命令,将纯文本分隔结果解析为数组数组。

fmPromise.executeFileMakerDataAPI(query) 使用给定参数执行 FileMaker Data API 调用。

fmPromise.executeFileMakerDataAPIRecords(query) 这是 executeFileMakerDataAPI 的语法糖,当您想要的只是来自 datafieldData > 数组(大部分时间)。 门户阵列也被复制到现场数据。

fmPromise.configuration(schema) 获取当前 webViewer 实例的配置。 如果未保存任何配置,或者配置不包含所有必填字段,则会显示一个配置对话框。

fmPromise.loadModule(moduleName)fmPromiseModule 表中加载一条记录。 如果您有不想立即加载到每个模块的可选 JavaScript 代码,或者不想内联到所有 fmPromiseModule 记录的共享库,这将很有用。

Additional benefits

  • FileMaker worker scripts don't need to know anything about your web viewers, they simply exit with a (preferably JSON) result.
  • FileMaker Scripts can return an "Error …" result, which will be used to reject the script call's promise.
  • Each FileMaker script call has an id, so you can fire off multiple script calls to FileMaker and they will resolve correctly.
  • You can make script calls as soon as your <script> tag finishes loading, since fmPromise takes care of polling for the window.FileMaker object.

Caveats

  • The callback script defaults to looking for a webViewer named fmPromiseWebViewer. It would be nice if the JavaScript FileMaker object had this as a property. You can override the web viewer name in the JavaScript.
  • Whe you use Perform Javascript in Web Viewer, you will not get a result if the script your are calling is an async script.

Getting Started

创建静态 HTML 文件并包含 fm-promise.js 文件。 为您自己的 JavaScript 添加另一个

示例:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="fm-promise.js"></script>
</head>
<body>
</body>
<script>
    'use strict';

    async function hello() {
        const name = await fmPromise.evaluate('Get(Username)');
        document.body.innerText = 'Hello, ' + name;
    }

    hello();
</script>
</html>

现在我们想在 FileMaker 的 Web 查看器中显示它。

将 Web 查看器组件添加到您的 FileMaker 布局。 现在,网址可以是指向您的 HTML 文件的文件,例如 "file:///Users/myUserName/MyProject/hello-fmpromise.html"

重要:选中标记为“允许 JavaScript 执行 FileMaker 脚本”的框。 没有这一步,什么都不会发生。

重要提示:在“位置”检查器中,为您的网络查看器指定名称 fmPromiseWebViewer

现在您应该能够进入浏览模式并看到显示的问候消息。

下面从 Internet 加载 vue.js,然后从您的 FileMaker 解决方案中获取所有表格和字段,并将它们显示为嵌套列表。

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="fm-promise.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        async function fetchSchema() {
            const rows = await fmPromise.executeSql('select tableName, fieldName from filemaker_fields');
            const tables = rows.reduce((result, eachRow) => {
                let tableName = eachRow[0];
                let fieldName = eachRow[1];
                const tbl = result[tableName] || (result[tableName] = {name: tableName, fields: []});
                tbl.fields.push({name: fieldName});
                return result;
            }, {});

            new Vue({
                el: '#app',
                data: {tables}
            })
        }

        fetchSchema();
    </script>
</head>
<body>
<div id="app">
    <ol>
        <li v-for="table in tables">
            {{ table.name }}
            <ul>
                <li v-for="field in table.fields">
                    {{ field.name }}
                </li>
            </ul>
        </li>
    </ol>
</div>
</body>
</html>

fmPromise: a Richer JavaScript Web Viewer Integration

FileMaker 19 has added the ability to call FileMaker scripts from your JavaScript, as well as executing a JavaScript function from a FileMaker script. This allows for integration between FileMaker and JavaScript/WebViewer, but has some clunky bits that we can make better.

  • Every call from JavaScript to a FileMaker script needs a public JavaScript function to receive the script response, and it is the responsibility of the Script to call that function.
  • All data coming back from FileMaker is a string
  • Debugging JavaScript errors is very difficult without browser-based dev tools
  • The window.FileMaker object is not available right when the page loads, so you need a window.setTimeout() to wait for it to become available if you want to populate your web viewer using a script call.

fmPromise is designed to address these shortcomings, and help you utilize web viewers in your solution with the minimum amount of fuss.

Long story short, instead of JavaScript like this:

function submitMyOrder(orderDetails) {
    window.progressDialog = showProgressDialog('Submitting...'); // global scope, not ideal
    const scriptParam = JSON.stringify(orderDetails); // convert JS object to string
    try {
        window.FileMaker.PerformScript('Submit Order from WebViewer', scriptParam); // no return value
    } catch (e) {
        showError('Could not call script "Submit Order from WebViewer", was it renamed?" ' + e);
    }
}

// the FileMaker `Submit Order from WebViewer` script is responsible for calling this on success 
function submitOrderSuccessCallback(payloadString) {
    const submitResult = JSON.parse(payloadString); // convert string to JS objects
    showSubmitResult(submitResult);
    window.progressDialog.close();
}

// the FileMaker `Submit Order from WebViewer` script is responsible for calling this on failure 
function submitOrderErrorCallback(msg) {
    showError('Could not send order: ' + msg);
    window.progressDialog.close();
}

With fmPromise, the call to fmPromise.performScript() returns a Promise complete with error handling. This lets you write JavaScript like this:

// WITH fmPromise, the call to performScript returns a Promise
// the `ScriptResult` of `Find Matching Contacts` will be parsed as JSON and used to resolve the Promise
function submitMyOrder(orderDetails) {
    const progressDialog = showProgressDialog('Submitting...'); // block scope
    fmPromise.performScript('Submit Order from WebViewer', orderDetails) // returns a Promise
        .then(function (submitResult) {
            showSubmitResult(submitResult);
        })
        .catch(function (error) {
            showError('Could not send order: ' + error);
        }).finally(function () {
        progressDialog.close();
    })
}

Add in the syntactic sugar of async/await and you can have this:

async function submitMyOrder(orderDetails) {
    const progressDialog = showProgressDialog('Submitting...');
    try {
        const submitResult = await fmPromise.performScript('Submit Order from WebViewer', orderDetails);
        showSubmitResult(submitResult);
    } catch (error) {
        showError('Could not send order: ' + msg);
    } finally {
        progressDialog.close();
    }
}

Debugging

I would strongly recommend you enable external JavaScript debugging in your web viewer, as described here

From your terminal, type:

defaults write com.FileMaker.client.pro12 WebKitDebugDeveloperExtrasEnabled -bool YES

This allows you to utilize Safari's developer tools on your web viewer code, which is incredibly useful.

Packaging

The FMPromise Add-On workflow is:

  1. Create a new module, which writes my-module.html to your Documents/fmPromise/ directory.
  2. Edit this file and preview it in $$FMPROMISE_DEVMODE
  3. Once satisfied, package the module into the fmPromiseModule table

This packaging step gets the source of your .html file, and optionally inlines any external JavaScript / CSS files.

If you want to change the inline behavior of a script or style, add a data-package attribute to your <script> or <link> tag containing your JavaScript / CSS.

  • data-package="omit" will remove the tag entirely. This is handy for things which you only want present in dev mode, like Vue Dev Tools.
  • data-package="leave" will not inline the file, but it will remaing as an external resource. This is good for large external libraries, but means your module will probably not work without internet access.

API

fmPromise.performScript(scriptName, parameter, option) Performs a FileMaker script, returning a Promise. The Promise will be resolved with the script result (parsed as JSON if possible), or rejected if the FileMaker script result starts with the word "ERROR". The third option parameter specifies how to handle FileMaker scripts which are already running. See FileMaker 9.1.2 release notes for details.

fmPromise.evaluate(expression, letVars) Evaluate an expression in FileMaker using optional letVars. This is also a handy way to set $$GLOBAL variables.

fmPromise.insertFromUrl(url, curlOptions) Inserts from URL without worrying about cross-site scripting limitations imposed on the web viewer.

fmPromise.setFieldByName(url, curlOptions) Sets a field by name in FileMaker.

fmPromise.executeSql(sql, ...bindings) Executes a SQL command with placeholders, parsing the plain-text delimited result into an array of arrays.

fmPromise.executeFileMakerDataAPI(query) Execute a FileMaker Data API call with the given parameter.

fmPromise.executeFileMakerDataAPIRecords(query) This is syntactic sugar for the executeFileMakerDataAPI when all you want is the fieldData from the data array (which is most of the time). Portal arrays are also copied to the field data.

fmPromise.configuration(schema) Gets a configuration for the current webViewer instance. If no configuration has been saved, or the configuration does not have all the required fields, a configuration dialog is displayed.

fmPromise.loadModule(moduleName) Loads a record from the fmPromiseModule table. This is useful if you have optional JavaScript code which you don't want to load into every module immediately, or a shared library which you don't want to inline into all of your fmPromiseModule records.

Additional benefits

  • FileMaker worker scripts don't need to know anything about your web viewers, they simply exit with a (preferably JSON) result.
  • FileMaker Scripts can return an "Error …" result, which will be used to reject the script call's promise.
  • Each FileMaker script call has an id, so you can fire off multiple script calls to FileMaker and they will resolve correctly.
  • You can make script calls as soon as your <script> tag finishes loading, since fmPromise takes care of polling for the window.FileMaker object.

Caveats

  • The callback script defaults to looking for a webViewer named fmPromiseWebViewer. It would be nice if the JavaScript FileMaker object had this as a property. You can override the web viewer name in the JavaScript.
  • Whe you use Perform Javascript in Web Viewer, you will not get a result if the script your are calling is an async script.

Getting Started

Create a static HTML file and include the fm-promise.js file. Add another <script> block for your own JavaScript. Your script can utilize fmPromise immediately on page load to do things like call FileMaker scripts, evaluate expressions, and execute SQL.

Example:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="fm-promise.js"></script>
</head>
<body>
</body>
<script>
    'use strict';

    async function hello() {
        const name = await fmPromise.evaluate('Get(Username)');
        document.body.innerText = 'Hello, ' + name;
    }

    hello();
</script>
</html>

Now we want to display this in a Web Viewer in FileMaker.

Add a Web Viewer component to your FileMaker layout. For now, the Web Address can be a file pointing to your HTML file, e.g. "file:///Users/myUserName/MyProject/hello-fmpromise.html".

IMPORTANT: check the box labeled "Allow JavaScript to perform FileMaker Scripts". Without this step, nothing will happen.

IMPORTANT: in the "Position" inspector, give your web viewer the Name fmPromiseWebViewer.

Now you should be able to go to browse mode and see the hello message displayed.

The following loads vue.js from the internet and then fetches all tables and fields from your FileMaker solution and displays them as nested lists.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="fm-promise.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        async function fetchSchema() {
            const rows = await fmPromise.executeSql('select tableName, fieldName from filemaker_fields');
            const tables = rows.reduce((result, eachRow) => {
                let tableName = eachRow[0];
                let fieldName = eachRow[1];
                const tbl = result[tableName] || (result[tableName] = {name: tableName, fields: []});
                tbl.fields.push({name: fieldName});
                return result;
            }, {});

            new Vue({
                el: '#app',
                data: {tables}
            })
        }

        fetchSchema();
    </script>
</head>
<body>
<div id="app">
    <ol>
        <li v-for="table in tables">
            {{ table.name }}
            <ul>
                <li v-for="field in table.fields">
                    {{ field.name }}
                </li>
            </ul>
        </li>
    </ol>
</div>
</body>
</html>
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文