يوضّح هذا الدليل كيفية التحقّق من صحة متغيّر إدخال.
عند تحديد متغيّر إدخال، من أفضل الممارسات التحقّق من أنّ المستخدم
يدخل قيمة مناسبة. على سبيل المثال، إذا طلبت من المستخدم إدخال رقم، سيؤدي التحقّق من إدخاله 1 بدلاً من a إلى التأكّد من أنّ خطوتك تعمل بدون أخطاء.
هناك طريقتان للتحقّق من صحة متغيّر إدخال:
- التحقّق من صحة البيانات من جهة العميل: من خلال التحقّق من صحة البيانات من جهة العميل، يمكنك التأكّد من صحة البيانات التي يُدخلها المستخدم مباشرةً على جهازه. يتلقّى المستخدم ملاحظات فورية ويمكنه تصحيح أي أخطاء في البيانات التي أدخلها أثناء إعداد الخطوة.
- عملية التأكّد من صحة البيانات من جهة الخادم: تتيح لك عملية التأكّد من صحة البيانات من جهة الخادم تنفيذ منطق على الخادم أثناء عملية التأكّد من صحة البيانات، وهو أمر مفيد عندما تحتاج إلى البحث عن معلومات غير متوفّرة لدى العميل، مثل البيانات في أنظمة أو قواعد بيانات أخرى.
التحقّق من صحة البيانات من جهة العميل
تتوفّر طريقتان لتنفيذ عملية التحقّق من الصحة من جهة العميل:
- لإجراء عمليات التحقّق الأساسية، مثل التأكّد من أنّ إحدى الأدوات تحتوي على عدد أقل من عدد معيّن من الأحرف أو أنّها تحتوي على الرمز
@، استدعِ الفئةValidationمن خدمة "البطاقات" في إضافة Google Workspace. - لإجراء عملية تحقّق قوية، مثل مقارنة قيم عناصر واجهة المستخدم بقيم عناصر واجهة مستخدم أخرى، يمكنك إضافة عملية التحقّق باستخدام "لغة التعبير العادي" (CEL) إلى عناصر واجهة البطاقة المتوافقة التالية باستخدام
CardService.
استدعاء الفئة Validation
يتحقّق المثال التالي من أنّ أداة TextInput تحتوي على 10 أحرف أو أقل:
برمجة التطبيقات
const validation = CardService.newValidation().setCharacterLimit('10').setInputType(
CardService.InputType.TEXT);
للحصول على خيارات تحقّق إضافية، استخدِم التحقّق من صحة CEL.
التحقّق من صحة CEL
توفّر عملية التحقّق من صحة Common Expression Language (CEL) عمليات تحقّق فورية من البيانات المدخلة بدون تأخير عملية التحقّق من صحة البيانات من جهة الخادم، وذلك من خلال نقل عمليات التحقّق من قيم البيانات المدخلة التي لا تعتمد على البحث عن البيانات من خدمات أخرى إلى جهة العميل.
يمكنك أيضًا استخدام CEL لإنشاء سلوكيات البطاقات، مثل عرض أو إخفاء أداة حسب نتيجة التحقّق من الصحة. ويفيد هذا النوع من السلوك في عرض أو إخفاء رسالة خطأ تساعد المستخدمين في تصحيح إدخالاتهم.
يتضمّن إنشاء عملية تحقّق كاملة من صحة CEL المكوّنات التالية:
ExpressionDataفي البطاقة: يحتوي على منطق التحقّق من الصحة ومنطق تشغيل الأداة عندما يتم استيفاء أحد الشروط المحدّدة.Id: معرّف فريد لـExpressionDataضمن البطاقة الحالية.-
Expression: سلسلة CEL التي تحدّد منطق التحقّق (مثلاً،"value1 == value2"). Conditions: قائمة بالشروط التي تتضمّن مجموعة من نتائج التحقّق المحدّدة مسبقًا (SUCCESS أو FAILURE). ترتبط الشروط بجانب الأداةEventActionمن خلالTriggersباستخدامactionRuleIdمشترَك.- على مستوى البطاقة
EventAction: يتم تفعيل عمليات التحقّق من صحة بيانات CEL في البطاقة وربط الحقلExpressionDataبأدوات النتيجة من خلال مشغّلات ما بعد الحدث.actionRuleId: المعرّف الفريد لهذاEventAction.ExpressionDataAction: اضبط القيمة علىSTART_EXPRESSION_EVALUATIONللإشارة إلى أنّ هذا الإجراء يبدأ تقييم CEL.-
Trigger: يربطConditionsبـEventActionsمن جهة الأداة استنادًا إلىactionRuleId.
EventActionعلى مستوى الأداة: تتحكّم في سلوك أداة النتائج عند استيفاء شرط النجاح أو الفشل. يمكن أن يكون عنصر واجهة المستخدم الخاص بالنتائج، على سبيل المثال،TextParagraphيحتوي على رسالة خطأ لا تظهر إلا عند فشل عملية التحقّق.actionRuleId: تطابقactionRuleIdفيTriggerعلى جانب البطاقة.-
CommonWidgetAction: يحدّد الإجراءات التي لا تتضمّن تقييمات، مثل تعديل مستوى ظهور التطبيق المصغّر.-
UpdateVisibilityAction: إجراء يعدّل حالة ظهور الأداة (VISIBLE أو HIDDEN).
-
يوضّح المثال التالي كيفية تنفيذ عملية التحقّق من صحة CEL للتأكّد مما إذا كان إدخالان نصيان متساويين. تظهر رسالة خطأ إذا لم يكونا متساويين.
-
الشكل 1: عند استيفاء الشرط failCondition(عدم تساوي القيم المُدخلة)، يتم ضبط أداة رسالة الخطأ علىVISIBLEوتظهر. -
الشكل 2: عند استيفاء الشرط successCondition(تكون القيم المدخلة متساوية)، يتم ضبط أداة رسالة الخطأ علىHIDDENولا تظهر.
في ما يلي مثال على رمز التطبيق وملف بيان JSON:
برمجة التطبيقات
function onConfig() {
// Create a Card
let cardBuilder = CardService.newCardBuilder();
const textInput_1 = CardService.newTextInput()
.setTitle("Input field 1")
.setFieldName("value1"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
const textInput_2 = CardService.newTextInput()
.setTitle("Input field 2")
.setFieldName("value2"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
let sections = CardService.newCardSection()
.setHeader("Enter same values for the two input fields")
.addWidget(textInput_1)
.addWidget(textInput_2);
// CEL Validation
// Define Conditions
const condition_success = CardService.newCondition()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
.setExpressionDataCondition(
CardService.newExpressionDataCondition()
.setConditionType(
CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_SUCCESS));
const condition_fail = CardService.newCondition()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
.setExpressionDataCondition(
CardService.newExpressionDataCondition()
.setConditionType(
CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_FAILURE));
// Define Card-side EventAction
const expressionDataAction = CardService.newExpressionDataAction()
.setActionType(
CardService.ExpressionDataActionType.START_EXPRESSION_EVALUATION);
// Define Triggers for each Condition respectively
const trigger_success = CardService.newTrigger()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID");
const trigger_failure = CardService.newTrigger()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID");
const eventAction = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_EVALUATION_RULE_ID")
.setExpressionDataAction(expressionDataAction)
.addPostEventTrigger(trigger_success)
.addPostEventTrigger(trigger_failure);
// Define ExpressionData for the current Card
const expressionData = CardService.newExpressionData()
.setId("expData_id")
.setExpression("value1 == value2") // CEL expression
.addCondition(condition_success)
.addCondition(condition_fail)
.addEventAction(eventAction);
card = card.addExpressionData(expressionData);
// Create Widget-side EventActions and a widget to display error message
const widgetEventActionFail = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
.setCommonWidgetAction(
CardService.newCommonWidgetAction()
.setUpdateVisibilityAction(
CardService.newUpdateVisibilityAction()
.setVisibility(
CardService.Visibility.VISIBLE)));
const widgetEventActionSuccess = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
.setCommonWidgetAction(
CardService.newCommonWidgetAction()
.setUpdateVisibilityAction(
CardService.newUpdateVisibilityAction()
.setVisibility(
CardService.Visibility.HIDDEN)));
const errorWidget = CardService.newTextParagraph()
.setText("<font color=\"#FF0000\"><b>Error:</b> Please enter the same values for both input fields.</font>")
.setVisibility(CardService.Visibility.HIDDEN) // Initially hidden
.addEventAction(widgetEventActionFail)
.addEventAction(widgetEventActionSuccess);
sections = sections.addWidget(errorWidget);
card = card.addSection(sections);
// Build and return the Card
return card.build();
}
ملف البيان بتنسيق JSON
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "CEL validation example",
"logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
"useLocaleFromApp": true
},
"flows": {
"workflowElements": [
{
"id": "cel_validation_demo",
"state": "ACTIVE",
"name": "CEL Demo",
"description": "Demonstrates CEL Validation",
"workflowAction": {
"inputs": [
{
"id": "value1",
"description": "The first number",
"cardinality": "SINGLE",
"dataType": {
"basicType": "STRING"
}
},
{
"id": "value2",
"description": "The second number",
"cardinality": "SINGLE",
"dataType": {
"basicType": "STRING"
}
}
],
"onConfigFunction": "onConfig",
"onExecuteFunction": "onExecute"
}
}
]
}
}
}
أدوات وعمليات التحقّق من صحة CEL المتوافقة
أدوات البطاقات التي تتوافق مع التحقّق من صحة CEL
تتيح التطبيقات المصغّرة التالية التحقّق من صحة تعبيرات CEL:
TextInputSelectionInputDateTimePicker
عمليات التحقّق المتوافقة مع CEL
- العمليات الحسابية
+: تضيف رقمَين منint64أوuint64أوdouble.-: تطرح هذه الدالة رقمَين من النوعint64أوuint64أوdouble.*: تضرب هذه الدالة رقمَين من النوعint64أوuint64أوdouble./: تقسم عددَين من النوعint64أوuint64أوdouble(القسمة الصحيحة).%: تحسب هذه الدالة باقي القسمة بين عددَين من النوعint64أوuint64.-: تنفي رقمint64أوuint64.
- العمليات المنطقية:
&&: تنفّذ عمليةANDمنطقية على قيمتين منطقيتين.||: تنفّذ عمليةORمنطقية على قيمتين منطقيتين.!: تنفّذ عمليةNOTمنطقية على قيمة منطقية.
- عمليات المقارنة:
-
==: تتحقّق هذه السمة مما إذا كانت قيمتان متساويتَين. يتيح استخدام الأرقام والقوائم. !=: تتحقّق مما إذا كانت قيمتان غير متساويتين. يتيح استخدام الأرقام والقوائم.<: تتحقّق هذه الدالة ممّا إذا كان الرقم الأولint64أوuint64أوdoubleأصغر من الرقم الثاني.<=: تتحقّق مما إذا كان الرقم الأولint64أوuint64أوdoubleأصغر من الرقم الثاني أو يساويه.>: تتحقّق هذه الدالة ممّا إذا كان الرقم الأولint64أوuint64أوdoubleأكبر من الرقم الثاني.>=: تتحقّق هذه الدالة ممّا إذا كان الرقم الأولint64أوuint64أوdoubleأكبر من الرقم الثاني أو يساويه.
-
- عمليات القوائم:
in: تتحقّق مما إذا كانت قيمة معيّنة متوفّرة في قائمة. يتيح استخدام الأرقام والسلاسل والقوائم المتداخلة.size: تعرض عدد العناصر في قائمة. تتيح استخدام الأرقام والقوائم المتداخلة.
سيناريوهات التحقّق غير المتاحة من شهادة CEL
- أحجام الوسيطات غير صحيحة للعمليات الثنائية: تتطلّب العمليات الثنائية (على سبيل المثال،
add_int64، يساوي) وسيطتَين بالضبط. سيؤدي تقديم عدد مختلف من الوسيطات إلى حدوث خطأ. - أحجام وسيطات غير صحيحة للعمليات الأحادية: تتطلّب العمليات الأحادية (مثل
negate_int64) وسيطة واحدة بالضبط. سيؤدي تقديم عدد مختلف من الوسيطات إلى حدوث خطأ. - الأنواع غير المتوافقة في العمليات العددية: تقبل العمليات الثنائية والأحادية العددية وسيطات الأرقام فقط. سيؤدي تقديم أنواع أخرى (مثل القيمة المنطقية) إلى ظهور خطأ.
التحقّق على صعيد الخادم
باستخدام عملية التأكّد من صحة البيانات من جهة الخادم، يمكنك تنفيذ منطق من جهة الخادم من خلال تحديد
onSaveFunction() في رمز خطوتك. عندما ينتقل المستخدم بعيدًا عن بطاقة إعداد الخطوة، يتم تشغيل onSaveFunction() ويتيح لك التحقّق من إدخال المستخدم.
إذا كان إدخال المستخدم صالحًا، ارجع إلى saveWorkflowAction.
إذا كانت بيانات المستخدم غير صالحة، عليك عرض بطاقة إعدادات تعرض رسالة خطأ للمستخدم توضّح له كيفية حلّ المشكلة.
بما أنّ عملية التأكّد من صحة البيانات من جهة الخادم غير متزامنة، قد لا يعرف المستخدم بشأن خطأ الإدخال إلى أن ينشر الوكيل.
يجب أن يتطابق id لكل إدخال تم التحقّق من صحته في ملف البيان مع name لأداة بطاقة في الرمز.
يتحقّق المثال التالي من أنّ إدخال نص المستخدم يتضمّن علامة "@":
ملف البيان
يحدّد مقتطف ملف البيان onSaveFunction() باسم "onSave":
JSON
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "Server-side validation example",
"logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
"useLocaleFromApp": true
},
"flows": {
"workflowElements": [
{
"id": "server_validation_demo",
"state": "ACTIVE",
"name": "Email address validation",
"description": "Asks the user for an email address",
"workflowAction": {
"inputs": [
{
"id": "email",
"description": "email address",
"cardinality": "SINGLE",
"required": true,
"dataType": {
"basicType": "STRING"
}
}
],
"onConfigFunction": "onConfig",
"onExecuteFunction": "onExecute",
"onSaveFunction": "onSave"
}
}
]
}
}
}
رمز التطبيق
يتضمّن رمز الخطوة دالة باسم onSave(). تتحقّق هذه السمة من أنّ السلسلة التي أدخلها المستخدم تتضمّن الرمز @، وفي حال تضمّنها، يتم حفظ الخطوة. إذا لم يكن الأمر كذلك، سيتم عرض بطاقة إعدادات تتضمّن رسالة خطأ توضّح كيفية حلّ المشكلة.
برمجة التطبيقات
// A helper method to push a card interface
function pushCard(card) {
const navigation = AddOnsResponseService.newNavigation()
.pushCard(card);
const action = AddOnsResponseService.newAction()
.addNavigation(navigation);
return AddOnsResponseService.newRenderActionBuilder()
.setAction(action)
.build();
}
function onConfig() {
const emailInput = CardService.newTextInput()
.setFieldName("email")
.setTitle("User e-mail")
.setId("email");
const saveButton = CardService.newTextButton()
.setText("Save!")
.setOnClickAction(
CardService.newAction()
.setFunctionName('onSave')
)
const sections = CardService.newCardSection()
.setHeader("Server-side validation")
.setId("section_1")
.addWidget(emailInput)
.addWidget(saveButton);
let card = CardService.newCardBuilder()
.addSection(sections)
.build();
return pushCard(card);
}
function onExecute(event) {
}
/**
* Validates user input asynchronously when the user
* navigates away from a step's configuration card.
*/
function onSave(event) {
console.log(JSON.stringify(event, null, 2));
// "email" matches the input ID specified in the manifest file.
var email = event.formInputs["email"][0];
console.log(JSON.stringify(email, null, 2));
// Validate that the email address contains an "@" sign:
if (email.includes("@")) {
// If successfully validated, save and proceed.
const hostAppAction = AddOnsResponseService.newHostAppAction()
.setWorkflowAction(
AddOnsResponseService.newSaveWorkflowAction()
);
const textDeletion = AddOnsResponseService.newRemoveWidget()
.setWidgetId("errorMessage");
const modifyAction = AddOnsResponseService.newAction()
.addModifyCard(
AddOnsResponseService.newModifyCard()
.setRemoveWidget(textDeletion)
);
return AddOnsResponseService.newRenderActionBuilder()
.setHostAppAction(hostAppAction)
.setAction(modifyAction)
.build();
} else {
// If the input is invalid, return a card with an error message
const textParagraph = CardService.newTextParagraph()
.setId("errorMessage")
.setMaxLines(1)
.setText("<font color=\"#FF0000\"><b>Error:</b> Email addresses must include the '@' sign.</font>");
const emailInput = CardService.newTextInput()
.setFieldName("email")
.setTitle("User e-mail")
.setId("email");
const saveButton = CardService.newTextButton()
.setText("Save!")
.setOnClickAction(
CardService.newAction().setFunctionName('onSave')
)
const sections = CardService.newCardSection()
.setHeader("Server-side validation")
.setId("section_1")
.addWidget(emailInput)
.addWidget(textParagraph) //Insert the error message
.addWidget(saveButton);
let card = CardService.newCardBuilder()
.addSection(sections)
.build();
const navigation = AddOnsResponseService.newNavigation()
.pushCard(card);
const action = AddOnsResponseService.newAction()
.addNavigation(navigation);
const hostAppAction = AddOnsResponseService.newHostAppAction()
.setWorkflowAction(
AddOnsResponseService.newWorkflowValidationErrorAction()
.setSeverity(AddOnsResponseService.ValidationErrorSeverity.CRITICAL)
);
return AddOnsResponseService.newRenderActionBuilder()
.setHostAppAction(hostAppAction)
.setAction(action)
.build();
}
}