宜搭 JS-API
本文書では、宜搭プラットフォームの JS パネルまたは変数バインドポップアップで直接呼び出すことのできる API およびその使用方法を主に紹介します。各 API には、API の具体的な使用方法を示すサンプルが添付されており、サンプルでは、モーションパネルの実際の使用状況をシミュレートするために以下の関数構造でラップしています(ラップされた関数名は実際の環境では自由に定義できます)。
export function someFunctionName() {
...
}
事前準備
以下の API では、JavaScript の基本的な知識が必要であり、一般的なデータ型、変数と関数の宣言および使用方法を理解している必要があります。また、JavaScript でよくある落とし穴を把握し、回避できる必要があります。
以下の API でよく見られる this.state、this.setState、this.$() を例に挙げると、this がイベント関数の最外層に出現する場合、this は正しい実行コンテキストを指しており、データソースの読み取り、データソースの設定、および他のフォームデータの取得を適切に実行できます。
export function setSomeValue() {
const status = this.state.status;
const newStatus = status + 1;
this.setState({ status: newStatus });
this.$('numberField_xxx').setValue(newStatus);
}
しかし、this がネストされた関数に出現する場合、this の参照先が正しいかどうかに注意が必要です。
export function setSomeValue(value) {
// ここに this の参照を保存
const that = this;
this.dataSourceMap.xxx.load(function (ret) {
// エラー !!!function は新しいコンテキスト環境を作成する
// ここでの this は変更され、データソースを読み込んだり他のフィールドを取得したりできません
this.$('numberField_xxx').setValue(ret);
// 代わりの方法として、外部に保存した正しい参照を使用する
that.$('numberField_xxx').setValue(ret);
});
// またはアロー関数を使用して this 値の変更を回避する
this.dataSourceMap.xxx.load((ret) => {
// アロー関数は新しいコンテキストを作成せず、this は変更されません
this.$('numberField_xxx').setValue(ret);
});
}
JavaScript 入門ガイドをいくつか紹介します:
グローバル変数 API
宜搭の設計パターンは主に React の方式を参考にしており、ページレベルの状態管理のためにグローバル変数を提供し、ページの再描画をトリガーするための対応する API も提供しています(具体的な使用方法は グローバル変数ドキュメント を参照)。
this.state.xxx
グローバル変数の値を取得します(React の API と一致)。
xxx は通常、ページのデータソースの変数名です。
例:
export function getState() {
// ページグローバル変数の値を取得し、console に表示
const status = this.state.status;
console.log( `status: ${status}` )
}
this.setState()
グローバル変数の値を設定し、ページの再描画をトリガーします(React の API と基本的に一致)。
注意:変数の値を変更するために this.state.a = b のような方法を使用しないでください。将来のアップグレードでは互換性を保証できず、関連コードは正常に動作しなくなります。
例:
export function setStateValue() {
// ページグローバル変数の値を設定し、ページの再描画をトリガー
this.setState({
status: 'loading',
text: '読み込み中…'
});
}
リモートデータ API
宜搭はリモートデータソースの設定をサポートしており、js を介してリモートデータソースの呼び出しをトリガーする API を提供します(具体的な使用方法は リモート API ドキュメント を参照)。
this.dataSourceMap.xxx.load()
指定されたリモート API を手動で呼び出します。xxx はデータソースパネルで設定されたデータソース名です。リクエストパラメータの渡しもサポートされており、API 呼び出しで渡されたリクエストパラメータはデータソース設定のリクエストパラメータとマージされてリクエストが送信されます。load メソッドは Promise を返します。
例:
export function fetchData() {
// データソースで設定された getDataList リモート API をリクエストし、pageSize と page パラメータを渡します。リクエストが成功した場合、console に結果を表示し、リクエストが失敗した場合、ポップアップで通知します
this.dataSourceMap.getDataList.load({
pageSize: 10,
page: this.state.currentPage
}).then((res) => {
if (res) {
console.log('fetchData', res);
}
}).catch((err) => {
this.utils.toast({
type: 'error',
title: 'リクエスト失敗!'
});
});
}
this.reloadDataSource()
自動読み込み設定が true に設定されているすべてのリモート API を再リクエストします。このメソッドも Promise を返します。
例:
export function reload() {
// すべての初期リクエストを再リクエストし、リクエスト成功後にポップアップで通知
this.reloadDataSource().then(res => {
this.utils.toast({
type: 'success',
title: '更新成功!'
});
});
}
JS 呼び出し API
宜搭はモーションパネルを使用して JS コードの記述を提供し、モーションパネルの関数は変数バインドおよびモーションバインドの使用に加えて、関数間の相互呼び出しもサポートします。
this.methodName()
モーションパネルの js 関数の関連呼び出し方法を提供します。ユーザーは this.xxx() を使用して、モーションパネルの他の関数を呼び出すことができます。xxx は他の関数の名前です。
例:
export function hello(params) {
this.utils.toast({
title: `hello ${params}` ,
type: 'success'
})
}
export function onClickInvoke(){
const value = this.$('textField_k1u12o6l').getValue()
// モーションパネルの他の関数を呼び出す
this.hello(value)
}
ユーティリティクラス関連 API
宜搭は多くの組み込みユーティリティ関数を提供しており、ユーザーが一般的な機能をより適切に実装できるように支援します。
this.utils.dialog()
ダイアログをポップアップ表示します。以下図参照、ユーザーは手動で閉じる必要があります:

宜搭は内部的に fusion コンポーネントを使用して実装しており、すべての Dialog コンポーネントの属性を設定できます。 ドキュメントアドレス、以下に一般的な属性を示します:
| パラメータ | 属性 | デフォルト値 | 説明 |
|---|---|---|---|
| type | 'alert', 'confirm', 'show' | 'alert' | - |
| title | (String) | - | - |
| content | (String|ReactNode) | - | HTML/JSX を渡して複雑なレイアウトを実現することも可能 |
| hasMask | (Boolean) | true | マスクの有無 |
| footer | (Boolean) | true | フッター操作ボタンの有無 |
| footerAlign | 'left', 'center', 'right' | 'right' | フッター操作の配置方向 |
| footerActions | ['cancel', 'ok'], ['ok', 'cancel'], ['ok'], ['cancel'] | - | フッター操作の種類と順序 |
| onOk | (Func) | - | OKクリック時のコールバック関数 |
| onCancel | (Func) | - | キャンセルクリック時のコールバック関数 |
例:
export function popDialog(){
this.utils.dialog({
type: 'confirm',
title: 'title',
content: 'content', // 改行が必要な場合はHTML/JSXを渡して実現可能
onOk: () => { },
onCancel: () => { },
});
}
// ダイアログの手動クローズをサポート
export function closeDialog() {
// dialogの戻り値を受け取る、この値はオブジェクト
const dialog = this.utils.dialog({});
// 適切なタイミングでオブジェクトのhideメソッドを呼び出してダイアログを閉じる
dialog.hide();
}
this.utils.formatter()
日付、金額、電話番号などをフォーマットするための一般的な formatter 関数です。
例:
export function format() {
// 日付フォーマット、出力値:2022-01-29
const formatDate = this.utils.formatter('date', new Date(), 'YYYY-MM-DD');
// 日付フォーマット、出力値:2022/01/29
const formatDate = this.utils.formatter('date', new Date(), 'YYYY/MM/DD');
// 日付フォーマット、出力値:2022-01-29 13:01:02
const formatDate2 = this.utils.formatter('date', new Date(), 'YYYY-MM-DD HH:mm:ss');
// 金額フォーマット、出力値:10, 000.99
const formatMoney = this.utils.formatter('money', '10000.99', ', ');
// 電話フォーマット、出力値:+86 1565 2988 282
const formatPhoneNumber = this.utils.formatter('cnmobile', '+8615652988282');
// カード番号フォーマット、出力値:1565 2988 2821 2233
const formatCardNumber = this.utils.formatter('card', '1565298828212233');
}
this.utils.getDateTimeRange(when, type)
現在または指定日時の開始終了期間のタイムスタンプを取得します。
when と type はどちらもオプション、デフォルトでは当日の開始終了期間を返します。日付と期間タイプを指定できます。
| パラメータ | 属性 | デフォルト値 | 説明 |
|---|---|---|---|
| when | タイムスタンプ、Date日付タイプをサポート | 現在時刻 new Date() | 指定日 |
| type | 'year', 'month', 'week', 'day', 'date', 'hour', 'minute', 'second' | 'day' | 獲得する期間タイプ |
例:
export function search() {
const [dayStart, dayEnd] = this.utils.getDateTimeRange();
console.log( `dayStart: ${dayStart}, dayEnd: ${dayEnd}` );
// 当日の開始終了タイムスタンプを出力
const [monthStart, monthEnd] = this.utils.getDateTimeRange(new Date(), 'month');
console.log( `monthStart: ${monthStart}, dayEnd: ${monthEnd}` );
// 当月の開始終了タイムスタンプを出力
}
this.utils.getLocale()
現在のページの言語環境を取得します。
例:
export function locale() {
const locale = this.utils.getLocale();
console.log( `locale: ${locale}` );
// 出力:locale: zh_CN
}
this.utils.getLoginUserId()
ログインユーザーIDを取得します。
例:
export function getUserInfo() {
const userId = this.utils.getLoginUserId();
console.log( `userId: ${userId}` );
// 出力:userId: 43314767738888
}
this.utils.getLoginUserName()
ログインユーザー名を取得します。
例:
export function getUserInfo() {
const userName = this.utils.getLoginUserName();
console.log( `userName: ${userName}` );
// 出力:userName: 韩火火
}
this.utils.isMobile()
現在のアクセス環境がモバイル端末かどうかを判定します。
例:
export function someFunctionName() {
console.log('isMobile', this.utils.isMobile());
}
this.utils.isSubmissionPage()
現在のページがデータ送信ページかどうかを判定します。
例:
export function someFunctionName() {
console.log('isSubmissionPage', this.utils.isSubmissionPage());
}
this.utils.isViewPage()
現在のページがデータ表示ページかどうかを判定します。
例:
export function someFunctionName() {
console.log('isViewPage', this.utils.isViewPage());
}
this.utils.loadScript()
リモートスクリプトを動的に読み込みます。
例:
export function didMount() {
this.utils.loadScript('https://g.alicdn.com/code/lib/qrcodejs/1.0.0/qrcode.min.js').then(() => {
var qrcode = new QRCode(document.getElementById('qrcode'), {
text: "http://jindo.dev.naver.com/collie",
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
});
}
this.utils.openPage()
新しいページを開きます。
もし钉钉環境であれば、钉钉 API を使用して新しいページを開き、より快適な体験を提供します。
例:
export function someFunctionName() {
this.utils.openPage('/workbench');
}
this.utils.previewImage()
画像プレビュー。この API を使用すると、シンプルな画像プレビュー効果を実現できます。以下図参照:

例:
export function previewImg() {
this.utils.previewImage({ current: 'https://img.alicdn.com/tfs/TB1JUnZ2GL7gK0jSZFBXXXZZpXa-260-192.png_.webp' });
}
this.utils.toast()
情報通知。Dialog ダイアログよりも軽量で、ポップアップ後しばらくすると自動的に消えます。以下図参照:

パラメータ設定:
| パラメータ | 属性 | デフォルト値 | 説明 |
|---|---|---|---|
| type | 'success', 'warning', 'error', 'notice', 'help', 'loading' | 'notice' | - |
| title | (String) | - | - |
| size | 'medium', 'large' | 'medium' | - |
| duration | (Number) | - | type が loding の場合は無効 |
例:
export function popToast(){
this.utils.toast({
title: 'success',
type: 'success',
size: 'large',
})
}
// 手動でクローズメソッドを呼び出すことをサポート
export function showLoadingToast() {
// 戻り値を受け取る、この戻り値はクローズメソッド
const close = this.utils.toast({
title: '読み込み中',
type: 'loading',
size: 'large',
});
// 適切なタイミングでクローズメソッドを呼び出す
setTimeout(close, 3000);
}
ルーティング関連 API
宜搭はルーティング情報の取得およびページ遷移関連 API を提供しており、内部実装は主に react-router を使用しているため、遷移 API は react-router API と基本的に一致します。さらに、宜搭はルーティングに関連する拡張 API も提供しています。
this.utils.router.push()
ページ遷移し、遷移履歴をルーティングスタックに push します。ブラウザの戻るボタンで戻ることができます。push のパラメータ説明は以下の通りです:
function push(path: string, params?: object, blank?: boolean, isUrl?: boolean, type?: string) => void;
| パラメータ名 | タイプ | 必須 | 説明 |
|---|---|---|---|
| path | string | はい | 遷移先アドレス。完全な url、url フラグメント、pageID で構成される文字列も可能です。slug がある場合は、優先的に slug(ページ別名、宜搭では設定開放されていません)で遷移します。isUrl パラメータが true の場合、url 方式で解析されます。それ以外は pageId 形式で解析し、内部ページ間遷移を実現します。 |
| params | object | いいえ | 遷移アドレスに付与するクエリパラメータ {q: 'a', r: 'b'} は ?q=a&r=b と同等 |
| blank | boolean | いいえ | 新しいページを開くかどうか、デフォルトは false |
| isUrl | boolean | いいえ | url アドレスかどうか、デフォルトは false |
| type | string | いいえ | 選択可能値 push または replace、push 方式または replace 方式で遷移 |
例:
export function pushUrl() {
// ページ遷移し、fromSource パラメータを注入、最終的に遷移先アドレスは:https://www.aliwork.com?formSource=customPage
this.utils.router.push('https://www.aliwork.com', {fromSource: 'customPage'});
}
this.utils.router.replace()
ページ置換。router.push との違いは、この API は現在のページを次のページに移動するのではなく置換するため、ブラウザの戻るボタンで戻ることはできません。同等:
this.utils.router.push(path, params, false, false, 'replace');
例:
export function replaceUrl() {
// ページ遷移し、fromSource パラメータを注入
this.utils.router.replace('https://www.aliwork.com', {fromSource: 'customPage'});
}
this.utils.router.getQuery()
ページ URL パラメータを取得します。key パラメータを渡すと定義されたパラメータ値を返し、それ以外は URL のすべてのパラメータを返します。getQuery のパラメータ説明は以下の通りです:
function getQuery(key?: string, queryStr?: string) => Record<string, string> | string | undefined;
| パラメータ名 | タイプ | 必須 | 説明 |
|---|---|---|---|
| key | string | いいえ | key を渡すと対応する値を返し、それ以外はオブジェクト全体を返す |
| queryStr | string | いいえ | デフォルト値:location.search + location.hash, hash は search を上書きする。カスタム文字列解析をサポート、形式は '?a=1&b=2' |
例:
export function getQuery() {
// URL の fromSource パラメータを取得
const fromSource = this.utils.router.getQuery('fromSource');
console.log( `fromSource: ${fromSource}` );
}
this.utils.router.stringifyQuery()
URL パラメータをシリアライズします。つまり、オブジェクトを URL パラメータ形式に変換します。
例:
export function stringifyQuery() {
// オブジェクトを URL パラメータ形式にシリアライズし、console に表示
const params = {
name: 'yida',
gender: 'm'
};
const urlStr = this.utils.router.stringifyQuery(params);
console.log( `urlParams: ${urlStr}` );
// 出力結果:urlParams: name=yida&gender='m'
}
コンポーネント共通 API
コンポーネント関連の API を説明する前にいくつかの概念を事前紹介する必要があります:
- コンポーネントのユニーク識別子(fieldId)- 宜搭はすべてのコンポーネントに一意の識別子を設定し、コンポーネントインスタンスを識別するために使用され、コンポーネントのユニーク識別子はコンポーネントプロパティパネルで確認できます。
- コンポーネント属性(prop)- 宜搭では、すべてのコンポーネントはコンポーネント属性を設定することで異なる機能を実現できます(React の props と似ています)。コンポーネントプロパティパネルにホバーすることで、設定項目に対応する属性名を確認できます。

コンポーネント共通 API は、宜搭が提供するすべてのコンポーネントで使用でき、主にコンポーネントの属性を読み取りまたは設定するために使用されます。
this.$(fieldId).get(prop)
fieldId でコンポーネントを見つけ、コンポーネントの属性値を取得します。fieldId はコンポーネント識別子、prop はコンポーネントの属性名です。
注意:属性値を読み取るために this.$(fieldId).xxx のような方法を使用しないでください。将来のアップグレードでは互換性を保証できず、関連コードは正常に動作しなくなります。
例:
export function getAttribute(){
// テキストコンポーネントの内容(content)属性を取得し、console に表示
const content = this.$('text_kyz78exo').get('content')
console.log( `text content: ${content}` );
}
this.$(fieldId).set(prop, value)
fieldId でコンポーネントを見つけ、コンポーネントの属性値を設定します。fieldId はコンポーネント識別子、prop はコンポーネント属性名、value は設定する属性値です。
注意:属性値を設定するために this.$(fieldId).xxx = xxx のような方法を使用しないでください。将来のアップグレードでは互換性を保証できず、関連コードは正常に動作しなくなります。
例:
export function setAttribute(){
// テキストコンポーネントの最大行数(maxLine)属性を設定
this.$('text_kyz78exo').set('maxLine', 5);
}
フォームコンポーネント API
フォームコンポーネントは宜搭プラットフォームで最も重要なコンポーネントの一つであり、通常はフォームコンポーネントを使用してデータを収集します。例:入力フィールド、ラジオボタン、チェックボックス、ドロップダウン選択など。本部分では主にフォームコンポーネント関連の API を紹介します。
this.$(fieldId)
コンポーネントインスタンスを取得します。fieldId はコンポーネントのユニーク識別子です。コンポーネント API を呼び出す前、通常は this.$(fieldId) を使用してまずコンポーネントインスタンスを取得し、その後 API 呼び出しを行います。
注意:ドキュメントに記載されていない API や属性を使用するために this.$(fieldId).xxx のような方法を使用しないでください。ドキュメントに記載されていない API や属性は内部実装であり、将来のアップグレードでは互換性を保証できず、関連コードは正常に動作しなくなります。
this.$(fieldId).getValue()
指定されたフォームコンポーネントの入力値を取得します。
例:
export function getValue(){
// 入力フィールドコンポーネントのユーザー入力値を取得し、console に表示
const value = this.$('textField_kyz78exp').getValue();
console.log( `input value: ${value}` );
}
this.$(fieldId).setValue()
指定されたフォームコンポーネントの入力値を設定します。setValue のパラメータ説明は以下の通りです:
interface IOptions {
doNotValidate: boolean; // 自動検証を阻止するかどうか、デフォルトは false
formatted: boolean; // 既にフォーマットされているかどうか デフォルトは false
triggerChange: boolean; // コンポーネント値変更イベントをトリガーするかどうか、デフォルトは true
};
/**
* @param {any} value 設定するフォーム値
* @param {IOptions} [options] 設定項目、オプション
*/
function setValue(value: any, options?: IOptions) => void;
例:
export function setValue(){
// 入力フィールドコンポーネントの値を「hello world」に設定
this.$('textField_kyz78exp').setValue('hello world');
}
this.$(fieldId).reset()
指定されたフォームコンポーネントの入力値をリセットします。reset のパラメータ説明は以下の通りです:
/**
* @param {boolean} toDefault フォームコンポーネントのデフォルト値にリセットするかどうか、デフォルトは true
*/
function reset(toDefault?: boolean) => void;
例:
export function reset() {
// 入力フィールドコンポーネントの値をリセット
this.$('textField_kyz78exp').reset();
}
this.$(fieldId).getBehavior()
指定されたフォームコンポーネントの現在の状態を取得します。フォームコンポーネントの状態には以下の選択肢があります:
- NORMAL - 通常状態、つまり入力状態;
- READONLY - 読み取り専用状態;
- DISABLED - 無効状態;
- HIDDEN - 非表示状態;
例:
export function getBehavior() {
// 入力フィールドコンポーネントの状態を取得し、表示
const behavior = this.$('textField_kyz78exp').getBehavior();
console.log( `text behavior: ${behavior}` );
}
this.$(fieldId).setBehavior()
指定されたフォームコンポーネントの状態を設定します。設定できる状態は getBehavior 部分の説明を参照してください。
例:
export function setBehavior() {
// 入力フィールドコンポーネントの状態を無効(DISABLED)状態に設定
this.$('textField_kyz78exp').setBehavior('DISABLED');
}
this.$(fieldId).resetBehavior()
指定されたフォームコンポーネントの状態をリセットします。
例:
export function resetBehavior() {
// 入力フィールドコンポーネントの状態をリセット
this.$('textField_kyz78exp').resetBehavior();
}
this.$(fieldId).validate()
指定されたフォームコンポーネントの検証を実行します。validate のパラメータ説明は以下の通りです:
/**
* @param {Array|null} errors エラー情報、エラーがなければ null
* @param {Object} values フォームコンポーネントの値
*/
function ValidateCallback(errors: string[] | null, values: object | null) => void
/**
* @param {Function} callback 検証のコールバックメソッド、オプション
*/
function validate(callback?: ValidateCallback) => void;
例:
export function validate() {
// 入力フィールドコンポーネントの検証を実行し、検証失敗した場合は console に errors と values を表示
this.$('textField_kyz78exp').validate((errors, values) => {
console.log(JSON.stringify({errors, values}, null, 2));
});
}
入力フィールドの検証ルールが携帯電話番号の場合、検証失敗時は以下の構造を表示します:
{
"errors": {
"textField_kyz78exp": {
"errors": [
"入力フィールドは有効な携帯電話番号形式ではありません"
]
}
},
"values": {
"textField_kyz78exp": "33"
}
}
this.$(fieldId).disableValid()
フォームコンポーネントの検証をオフにします。
例:
export function disableValid() {
this.$('textField_kyz78exp').disableValid();
}
this.$(fieldId).enableValid()
フォームコンポーネントの検証をオンにします。enableValid のパラメータ説明は以下の通りです:
/**
* @param {boolean} doValidate 即座に検証を実行するかどうか、オプション、デフォルトは false
*/
function enableValid(doValidate?: boolean) => void;
例:
export function enableValid() {
// 入力コンポーネントの検証をオンにし、即座に実行
this.$('textField_kyz78exp').enableValid(true);
}
this.$(fieldId).setValidation()
フォームコンポーネントの検証ルールを設定します。setValidation のパラメータ説明は以下の通りです:
interface IRule {
type: string; // 検証タイプ
param: any; // 検証タイプに対応するパラメータ
message: string; // エラーメッセージ
}
/**
* @param {IRule[]} rules 検証ルール、必須
* @param {boolean} [doValidate] 即座に検証を実行するかどうか、オプション、デフォルトは false
*/
function setValidation(rules: IRule[], doValidate?: boolean) => void;
宜搭がサポートする検証タイプは以下の通りです:
| サポートされている検証ルール | 属性 |
|---|---|
| 必須 | {"type": "required"} |
| 最小長 | {"type": "minLength", "param": "23" } |
| 最大長 | {"type": "maxLength", "param": "23" } |
| メール | {"type": "email"} |
| 携帯 | {"type": "mobile"} |
| URL | {"type": "url"} |
| 最小値 | {"type": "minValue", "param": "3"} |
| 最大値 | {"type": "maxValue", "param": "3"} |
| カスタム関数 | {"type": "customValidate", "param": (value, rule) => { return ture; }} |
例:
export function setValidation() {
// 入力コンポーネントの検証ルールを設定、必須、最大長は10、
this.$('textField_kyz78exp').setValidation([{
type: 'required'
}, {
type: 'maxLength',
param: '10'
}, {
type: 'customValidate',
param: (value, rule) => {
if(/^\d*$/.test(value)) {
return true;
}
return rule.message;
},
message: '数字のみ入力可能'
}]);
}
this.$(fieldId).resetValidation()
フォームコンポーネントの検証ルールをリセットします。setValidation 後に以前の検証ルールを回復するために使用されます。resetValidation のパラメータは以下の通りです:
/**
* @param {boolean} [doValidate] 即座に検証を実行するかどうか、オプション、デフォルトは false
*/
function resetValidation(doValidate?: boolean) => void;
例:
export function resetValiation() {
// 入力フィールドコンポーネントの検証ルールをリセットし、即座に検証
this.$('textField_kyz78exp').resetValidation(true);
}
Dialog コンポーネント API
宜搭は、ダイアログ形式のコンテンツ表示を行うためのダイアログコンポーネントを提供しており、ダイアログの動作を操作するためのいくつかの API も提供しています。
this.$(fieldId).show()
指定されたダイアログを表示します。この API は、ダイアログ表示後のコールバックを行う callback 関数を提供します。
例:
export function openDialog() {
this.$('dialog_kyz78exr').show(() => {
console.log('Dialog is open');
});
}
this.$(fieldId).hide()
指定されたダイアログを閉じます。
例:
export function closeDialog() {
this.$('dialog_kyz78exr').hide();
}