Skip to content

6. اعتبارسنجی سمت کلاینت با جاوااسکریپت

در فصل قبلی، اعتبارسنجی سمت سرور را بررسی کردیم. بیایید به معماری یک برنامه Spring MVC بازگردیم:

BD

تا اینجای کار، صفحاتی که به کلاینت ارسال شده‌اند هیچ جاوااسکریپتی نداشته‌اند. اکنون به بررسی این فناوری می‌پردازیم که در ابتدا به ما امکان انجام اعتبارسنجی سمت کلاینت را می‌دهد. اصل کار به این صورت است:

  • جاوااسکریپت مقادیر را به سرور وب ارسال می‌کند؛
  • و بنابراین، قبل از این POST، می‌تواند اعتبار داده‌ها را بررسی کرده و اگر داده‌ها نامعتبر باشند، از اجرای POST جلوگیری کند؛

ما از فرم‌هایی استفاده خواهیم کرد که در سمت سرور اعتبارسنجی شده‌اند. اکنون گزینه اعتبارسنجی آن‌ها را هم در سمت کلاینت و هم در سمت سرور فراهم خواهیم کرد.

توجه: این یک موضوع پیچیده است. خوانندگانی که به این موضوع علاقه‌مند نیستند می‌توانند مستقیماً به پاراگراف ۷ بروند.

6.1. ویژگی‌های پروژه

ما چند نما از پروژه را برای نشان دادن ویژگی‌های آن ارائه می‌کنیم. صفحهٔ اولیه با استفاده از URL و [http://localhost:8080/js01.html] تولید می‌شود.

 

اعتبارسنجی در هر دو سمت: سمت کلاینت و سمت سرور پیاده‌سازی شده است. از آنجایی که POST تنها در صورتی انجام می‌شود که مقادیر در سمت کلاینت معتبر تشخیص داده شده باشند، اعتبارسنجی سمت سرور همیشه با موفقیت انجام می‌شود. بنابراین ما لینکی برای غیرفعال کردن اعتبارسنجی سمت کلاینت فراهم کرده‌ایم. وقتی در این حالت باشید، رفتار همان است که قبلاً درباره‌اش بحث کردیم. در اینجا یک مثال آمده است:

123
  • در [1]، مقادیر وارد شده؛
  • در [2]، پیام‌های خطا مربوط به ورودی‌ها؛
  • در [3]، خلاصه‌ای از خطاها، به همراه جزئیات زیر برای هر یک:
    • نام فیلد اعتبارسنجی‌شده،
    • کد خطا،
    • پیام پیش‌فرض برای آن کد خطا؛

اکنون، بیایید اعتبارسنجی سمت کلاینت را فعال کنیم:

  • در [1]، مقادیر وارد شده. توجه داشته باشید که ورودی‌های نادرست به شیوه‌ای متفاوت قالب‌بندی شده‌اند؛
  • در [2]، پیام‌های خطا مرتبط با ورودی‌های نادرست. این پیام‌ها با آنچه توسط سرور تولید می‌شود یکسان هستند؛
  • در [3-4]، هیچ چیزی باقی نمی‌ماند زیرا تا زمانی که ورودی‌های نادرستی وجود داشته باشند، درخواست POST به سرور انجام نمی‌شود؛

6.2. اعتبارسنجی سمت سرور

6.2.1. پیکربندی

ما با ایجاد یک پروژه جدید Maven به نام [springmvc-validation-client] شروع می‌کنیم:

ما پروژه را به شرح زیر توسعه خواهیم داد:

  

کلاس [Config] پروژه را پیکربندی می‌کند. این کلاس دقیقاً مشابه آنچه در پروژه‌های قبلی بود است:


package istia.st.springmvc.config;


import java.util.Locale;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;

@Configuration
@ComponentScan({ "istia.st.springmvc.controllers", "istia.st.springmvc.models" })
@EnableAutoConfiguration
public class Config extends WebMvcConfigurerAdapter {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("i18n/messages");
        return messageSource;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        return localeChangeInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Bean
    public CookieLocaleResolver localeResolver() {
        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
        localeResolver.setCookieName("lang");
        localeResolver.setDefaultLocale(new Locale("fr"));
        return localeResolver;
    }

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".xml");
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setCacheable(true);
        templateResolver.setCharacterEncoding("UTF-8");
        return templateResolver;
    }

    @Bean
    SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }

}

کلاس [Main] کلاس اجرایی پروژه است:


package istia.st.springmvc.main;

import istia.st.springmvc.config.Config;

import java.util.Arrays;

import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;

public class Main {
    public static void main(String[] args) {
        // راه‌اندازی برنامه
        ApplicationContext context = SpringApplication.run(Config.class, args);
        // نمایش فهرست بیین‌های یافت‌شده توسط Spring
        System.out.println("Liste des beans Spring");
        String[] beanNames = context.getBeanDefinitionNames();
        Arrays.sort(beanNames);
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
  • خط ۱۳: Spring Boot با استفاده از فایل پیکربندی [Config] راه‌اندازی می‌شود؛
  • خطوط ۱۵–۲۰: برای این مثال، نحوه نمایش لیست اشیاء مدیریت‌شده توسط Spring را نشان می‌دهیم. این می‌تواند زمانی مفید باشد که احساس می‌کنید Spring یکی از کامپوننت‌های شما را مدیریت نمی‌کند. این روشی برای بررسی این موضوع است. همچنین روشی برای تأیید پیکربندی خودکار انجام‌شده توسط Spring Boot است. در کنسول، لیستی مشابه موارد زیر را مشاهده خواهید کرد:
Liste des beans Spring
basicErrorController
beanNameHandlerMapping
beanNameViewResolver
config
defaultServletHandlerMapping
defaultTemplateResolver
defaultViewResolver
dispatcherServlet
dispatcherServletRegistration
embeddedServletContainerCustomizerBeanPostProcessor
error
errorAttributes
faviconHandlerMapping
faviconRequestHandler
handlerExceptionResolver
hiddenHttpMethodFilter
http.mappers.CONFIGURATION_PROPERTIES
httpRequestHandlerAdapter
jacksonObjectMapper
jsController
layoutDialect
localeChangeInterceptor
localeResolver
mappingJackson2HttpMessageConverter
mbeanExporter
mbeanServer
messageConverters
messageSource
multipart.CONFIGURATION_PROPERTIES
multipartConfigElement
multipartResolver
mvcContentNegotiationManager
mvcConversionService
mvcUriComponentsContributor
mvcValidator
objectNamingStrategy
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperAutoConfiguration
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration$Empty
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$DefaultTemplateResolverConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafViewResolverConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebLayoutConfiguration
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$ObjectMappers
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store
org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
org.springframework.context.annotation.MBeanExportConfiguration
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
propertySourcesPlaceholderConfigurer
requestContextListener
requestMappingHandlerAdapter
requestMappingHandlerMapping
resourceHandlerMapping
serverProperties
simpleControllerHandlerAdapter
spring.mvc.CONFIGURATION_PROPERTIES
spring.resources.CONFIGURATION_PROPERTIES
templateEngine
templateResolver
thymeleafResourceResolver
thymeleafViewResolver
tomcatEmbeddedServletContainerFactory
viewControllerHandlerMapping
viewResolver

ما اشیاء تعریف‌شده در کلاس [Config] را برجسته کرده‌ایم.

6.2.2. مدل فرم

بیایید به کاوش پروژه ادامه دهیم:

  

کلاس [Form01] کلاسی است که مقادیر ارسال‌شده را دریافت می‌کند. این کلاس به شرح زیر است:


package istia.st.springmvc.models;

import java.util.Date;

import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.URL;
import org.springframework.format.annotation.DateTimeFormat;

public class Form01 {

    //مقادیر ارسال‌شده
    @NotNull
    @AssertFalse
    private Boolean assertFalse;
    
    @NotNull
    @AssertTrue
    private Boolean assertTrue;
    
    @NotNull
    @Future
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date dateInFuture;
    
    @NotNull
    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date dateInPast;
    
    @NotNull
    @Max(value = 100)
    private Integer intMax100;
    
    @NotNull
    @Min(value = 10)
    private Integer intMin10;
    
    @NotNull
    @NotBlank
    private String strNotEmpty;
    
    @NotNull
    @Size(min = 4, max = 6)
    private String strBetween4and6;
    
    @NotNull
    @Pattern(regexp = "^\\d{2}:\\d{2}:\\d{2}$")
    private String hhmmss;
    
    @NotNull
    @Email
    @NotBlank
    private String email;
    
    @NotNull
    @Length(max = 4, min = 4)
    private String str4;
    
    @Range(min = 10, max = 14)
    @NotNull
    private Integer int1014;
    
    @NotNull
    @DecimalMax(value = "3.4")
    @DecimalMin(value = "2.3")
    private Double double1;
    
    @NotNull
    private Double double2;
    
    @NotNull
    private Double double3;
    
    @URL
    @NotBlank
    private String url;
    
    // اعتبارسنجی کلاینت
    private boolean clientValidation = true;
    // محلی
    private String lang;
    ...
}

ما برخی اعتبارسنج‌هایی را می‌بینیم که قبلاً با آن‌ها مواجه شده‌ایم. همچنین قصد داریم مفهوم اعتبارسنجی خاص را معرفی کنیم. این نوع اعتبارسنجی را نمی‌توان با استفاده از یک اعتبارسنج از پیش تعریف‌شده رسمی‌سازی کرد. در اینجا، ما نیاز داریم که [double1+double2] در محدوده [10,13] قرار گیرد.

6.2.3. اعتبارسنج

اعتبارسنج [JsController] به شرح زیر است:

  

package istia.st.springmvc.controllers;

import istia.st.springmvc.models.Form01;
...

@Controller
public class JsController {

    @RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
    public String js01(Form01 formulaire, Locale locale, Model model) {
        setModel(formulaire, model, locale, null);
        return "vue-01";
    }
...

    //آماده‌سازی قالب نما برای view-01
    private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
...
    }
}
  • خط ۹، اقدام [/js01
  • خط ۱۰: یک شیء از نوع [Form01] نمونه برداری شده و به طور خودکار در قالب قرار می‌گیرد، که با کلید [form01] مرتبط است؛
  • خط ۱۰: لوکال و قالب در پارامترها تزریق می‌شوند؛
  • خط ۱۱: با استفاده از این اطلاعات، قالب آماده می‌شود؛
  • خط ۱۲: نمای [vue-01.xml] نمایش داده می‌شود؛

روش [setModel] به شرح زیر است:


    //آماده‌سازی قالب نمای view-01
    private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
        // فقط زبان‌های فرانسوی (fr-FR) و انگلیسی (en-US) پشتیبانی می‌شوند
        String language = locale.getLanguage();
        String country = null;
        if (language.equals("fr")) {
            country = "FR";
            formulaire.setLang("fr_FR");
        }
        if (language.equals("en")) {
            country = "US";
            formulaire.setLang("en_US");
        }
        model.addAttribute("locale", String.format("%s-%s", language, country));
        //هر پیامی
        if (message != null) {
            model.addAttribute("message", message);
        }
}
  • هدف متد [setModel] درج موارد زیر در قالب است:
    • اطلاعات مربوط به لوکال،
    • پیامی که به‌عنوان آخرین پارامتر ارسال شده است؛
  • خط ۱۴: اطلاعات مربوط به منطقه (زبان، کشور) در قالب درج می‌شود؛
  • خطوط ۱۶–۱۸: هر پیامی که به‌عنوان پارامتر ارسال می‌شود، در محیط (locale) درج می‌شود؛
  • خطوط ۸ و ۱۲: اطلاعات مربوط به منطقه (locale) همچنین در فرم [Form01] ذخیره می‌شود. جاوا اسکریپت از این اطلاعات استفاده خواهد کرد؛

مقادیر واردشده در فرم [vue-01.xml] به اکشن زیر، [/js02]، ارسال خواهند شد:


    @RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
    public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
        Form01Validator validator = new Form01Validator(10, 13);
        validator.validate(formulaire, result);
        ...
}
  • خط ۲: حاشیه‌نویسی [@Valid Form01 formulaire] تضمین می‌کند که مقادیر ارسال‌شده تحت بررسی اعتبارسنج‌های کلاس [Form01] قرار گیرند. ما می‌دانیم که یک اعتبارسنجی خاص [double1+double2] در محدوده [10,13] وجود دارد. وقتی به خط ۳ می‌رسیم، این اعتبارسنجی هنوز انجام نشده است؛
  • خط ۳: شیء زیر [Form01Validator] ایجاد می‌شود:
  

package istia.st.springmvc.validators;

import istia.st.springmvc.models.Form01;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class Form01Validator implements Validator {

    // دامنه اعتبارسنجی
    private double min;
    private double max;

    // سازنده
    public Form01Validator(double min, double max) {
        this.min = min;
        this.max = max;
    }

    @Override
    public boolean supports(Class<?> classe) {
        return Form01.class.equals(classe);
    }

    @Override
    public void validate(Object form, Errors errors) {
        // شیء اعتبارسنجی‌شده
        Form01 form01 = (Form01) form;
        // مقدار [double1]
        Double double1 = form01.getDouble1();
        if (double1 == null) {
            return;
        }
        // مقدار [double2]
        Double double2 = form01.getDouble2();
        if (double2 == null) {
            return;
        }
        // [double1+double2]
        double somme = double1 + double2;
        //اعتبارسنجی
        if (somme < min || somme > max) {
            errors.rejectValue("double2", "form01.double2", new Double[] { min, max }, null);
        }
    }

}
  • خط ۸: برای پیاده‌سازی یک اعتبارسنجی خاص، ما کلاسی را ایجاد می‌کنیم که رابط Spring با نام [Validator] را پیاده‌سازی می‌کند. این رابط دو متد دارد: [supports] در خط ۲۱ و [validate] در خط ۲۶؛
  • خطوط ۲۱–۲۳: متد [supports] یک شیء از نوع [Class] را می‌پذیرد. این متد باید true را برای نشان دادن اینکه از این کلاس پشتیبانی می‌کند، یا در غیر این صورت false را بازگرداند؛
  • خط ۲۲: ما بیان می‌کنیم که کلاس [Form01Validator] تنها اشیاء از نوع [Form01] را اعتبارسنجی می‌کند؛
  • خطوط ۱۵–۱۸: به یاد داشته باشید که می‌خواهیم محدودیت [double1+double2] را در بازه [10,13] پیاده‌سازی کنیم. به جای محدود کردن خود به این بازه، محدودیت [double1+double2] را در بازه [min, max] بررسی خواهیم کرد. به همین دلیل است که ما یک سازنده با این دو پارامتر داریم؛
  • خط ۲۶: متد [validate] با یک نمونه از شی اعتبارسنجی‌شده – در این مورد، یک نمونه از [Form01] – و با مجموعه خطاهای شناخته‌شده فعلی، [Errors errors]، فراخوانی می‌شود. اگر اعتبارسنجی انجام‌شده توسط متد [validate] ناموفق باشد، باید یک عنصر جدید در مجموعه [Errors errors] ایجاد کند؛
  • خط ۴۳: اعتبارسنجی ناموفق بود. یک عنصر با استفاده از متد [Errors.rejectValue] به مجموعه [Errors errors] اضافه می‌شود، با پارامترهای زیر:
    • پارامتر ۱: معمولاً نام فیلدی که حاوی خطا است. در اینجا، فیلدهای موجود در [double1, double2] آزمایش شده‌اند. می‌توان هر یک از این دو را مشخص کرد،
    • پیام خطای مرتبط یا دقیق‌تر بگوییم، کلید آن در فایل‌های پیام خارجی‌شده:

[messages_fr.properties]


form01.double2=[double2+double1] doit être dans l''intervalle [{0},{1}]

[messages_en.properties]


form01.double2=[double2+double1] must be in [{0},{1}

این‌ها پیام‌هایی هستند که با {0} و {1} پیکربندی شده‌اند. بنابراین باید دو مقدار برای این پیام ارائه دهید. این کاری است که پارامتر سوم متد [Errors.rejectValue] انجام می‌دهد.

    • پارامتر چهارم یک پیام خطای پیش‌فرض است؛

بیایید به اقدام [/js02] بازگردیم:


    @RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
    public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
        Form01Validator validator = new Form01Validator(10, 13);
        validator.validate(formulaire, result);
        if (result.hasErrors()) {
            StringBuffer buffer = new StringBuffer();
            for (ObjectError error : result.getAllErrors()) {
                buffer.append(String.format("[name=%s,code=%s,message=%s]", error.getObjectName(), error.getCode(), error.getDefaultMessage()));
            }
            setModel(formulaire, model, locale, buffer.toString());
            return "vue-01";
        } else {
            redirectAttributes.addFlashAttribute("form01", formulaire);
            return "redirect:/js01.html";
        }
}
  • خط ۴: اعتبارسنج [Form01Validator] با پارامترهای زیر اجرا می‌شود:
    • پارامتر ۱: ابجکتی که در حال اعتبارسنجی است،
    • پارامتر ۲: لیست خطاهای این شیء. این شیء [BindingResult result] به‌عنوان پارامتر به اکشن ارسال می‌شود. اگر اعتبارسنجی ناموفق باشد، این شیء یک خطای دیگر خواهد داشت؛
  • خط ۵: خطاهای اعتبارسنجی را بررسی می‌کنیم؛
  • خطوط ۷–۱۰: ما روی لیست خطاها حلقه می‌زنیم تا برای هر یک موارد زیر را ذخیره کنیم:
    • نام شیء اعتبارسنجی‌شده،
    • کد خطای آن،
    • پیام خطای پیش‌فرض آن؛
  • خط ۱۰: با استفاده از این اطلاعات، قالب نما [vue-01.xml] ساخته می‌شود. این بار، حاوی یک پیام واحد است: نسخه الحاق‌شده و مخفف‌شدهٔ پیام‌های خطای مختلف؛
  • خطوط ۱۲–۱۵: اگر همه مقادیر ارسال‌شده معتبر باشند، کلاینت به اکشن [/js01] هدایت می‌شود، در حالی که مقادیر ارسال‌شده به‌عنوان ویژگی‌های Flash تنظیم شده‌اند؛

6.2.4. نما

ویوی [vue-01.xml] پیچیده است. ما فقط بخش کوچکی از آن را ارائه می‌دهیم:


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Spring 4 MVC</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="/css/form01.css" />
        <script type="text/javascript" src="/js/jquery/jquery-1.10.2.min.js"></script>
        ...
    </head>
    <body>
        <!-- عنوان -->
        <h3>
            <span th:text="#{form01.title}"></span>
            <span th:text="${locale}"></span>
        </h3>
        <!-- menu -->
        <p>
...
        </p>
        <!-- فرم -->
        <form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
            <table>
                <thead>
                    <tr>
                        <th class="col1" th:text="#{form01.col1}">Contrainte</th>
                        <th class="col2" th:text="#{form01.col2}">Saisie</th>
                        <th class="col3" th:text="#{form01.col3}">Validation client</th>
                        <th class="col4" th:text="#{form01.col4}">Validation serveur</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- الزامی -->
                    <tr>
                        <td class="col1">required</td>
                        <td class="col2">
                            <input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
                        </td>
                        <td class="col3">
                            <span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
                        </td>
                        <td class="col4">
                            <span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
                        </td>
                    </tr>
...
                </tbody>
            </table>
            <p>
            <!-- دکمه ارسال -->
            <input type="submit" th:value="#{form01.valider}" value="Valider" onclick="javascript:postForm01()" />
            </p>
        </form>
        <!-- پیام اعتبارسنجی سمت سرور -->
        <br/>
        <fieldset class="fieldset">
            <legend>
                <span th:text="#{server.error.message}"></span>
            </legend>
            <span th:text="${message}" class="error"></span>
        </fieldset>
    </body>
</html>

این صفحه از تعدادی پیام موجود در فایل‌های پیام خارجی استفاده می‌کند:

[messages_fr.properties]


form01.title=Formulaire - Validations côté client - locale=
form01.col1=Contrainte
form01.col2=Saisie
form01.col3=Validation client
form01.col4=Validation serveur
form01.valider=Valider
server.error.message=Erreurs détectées par les validateurs côté serveur

[messages_en.properties]


form01.title=Form - Client side validation - locale=
form01.col1=Constraint
form01.col2=Input
form01.col3=Client validation
form01.col4=Server validation
form01.valider=Validate
server.error.message=Errors detected by the validators on the server side

بیایید به کد صفحه بازگردیم:

  • خط ۸: تعداد زیادی وارد کردن کتابخانهٔ جاوااسکریپت که می‌توانیم در اینجا نادیده بگیریم؛
  • خط ۱۴: نمایش می‌دهد locale را که توسط سرور در قالب تنظیم شده است؛
  • خط ۵۹: پیامی را که توسط سرور در قالب تنظیم شده است نمایش می‌دهد؛

کد در خطوط ۳۳–۴۴ جدید است. بیایید نگاهی به آن بیندازیم:


<!-- الزامی -->
<tr>
  <td class="col1">required</td>
  <td class="col2">
    <input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
  </td>
  <td class="col3">
    <span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
  </td>
  <td class="col4">
    <span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
  </td>
</tr>

شاید ساده‌ترین رویکرد این باشد که به کدی که توسط این قطعه Thymeleaf تولید شده است نگاه کنیم: HTML


<!-- الزامی -->
<tr>
  <td class="col1">required</td>
  <td class="col2">
    <input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" />
  </td>
  <td class="col3">
    <span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
  </td>
  <td class="col4">
                            
  </td>
</tr>

در سمت کلاینت، از کتابخانه‌ای به نام [jquery.validate] برای اعتبارسنجی استفاده خواهیم کرد. تمام ویژگی‌های [data-x] برای این کتابخانه هستند. وقتی اعتبارسنجی سمت کلاینت غیرفعال باشد، از این ویژگی‌ها استفاده نخواهد شد. بنابراین فعلاً نیازی به درک آن‌ها نیست. ما می‌توانیم به سادگی روی خط زیر از Thymeleaf تمرکز کنیم:


<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />

که خط زیر را تولید می‌کند: HTML:


<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" />

در مثال بالا، در تولید ویژگی [data-val-required="Le champ est obligatoire"] مشکلی وجود دارد. این به این دلیل است که مقدار مرتبط با این ویژگی از فایل‌های پیام خارجی تأمین می‌شود. بنابراین مجبوریم برای به‌دست آوردن آن از یک عبارت Thymeleaf استفاده کنیم. این عبارت عبارت زیر است: [th:attr="data-val-required=#{NotNull}"]. این عبارت ارزیابی شده و مقدار آن بدون تغییر در تگ تولیدشده HTML درج می‌شود. این تگ [th:attr] نامیده می‌شود زیرا برای تولید ویژگی‌هایی استفاده می‌شود که در Thymeleaf از پیش تعریف نشده‌اند. ما با ویژگی‌های از پیش تعریف‌شده‌ای مانند [th:text, th:value, th:class, ...] مواجه شده‌ایم، اما هیچ ویژگی‌ای به نام [th:data-val-required] وجود ندارد.

6.2.5. برگهٔ سبک

در بالا، کلاس‌هایی مانند CSS و [class="field-validation-valid"] را می‌بینیم. برخی از این کلاس‌ها توسط کتابخانه اعتبارسنجی JavaScript استفاده می‌شوند. آن‌ها در فایل زیر تعریف شده‌اند: [form01.css]:

  

@CHARSET "UTF-8";

/*سبک‌های سفارشی*/
body {
    background-image: url("/images/standard.jpg");
}

.col1 {
    background: lightblue;
}

.col2 {
    background: Cornsilk;
}

.col3 {
    background: AliceBlue;
}

.col4 {
    background: Lavender;
}

.error {
    color: red;
}

.fieldset{
    background: Lavender;
}
/* استایل‌ها برای کمک‌کننده‌های اعتبارسنجی
-----------------------------------------------------------*/
.field-validation-error {
    color: #f00;
}

.field-validation-valid {
    display: none;
}

.input-validation-error {
    border: 1px solid #f00;
    background-color: #هزینه؛
}

.validation-summary-errors {
    font-weight: bold;
    color: #f00;
}

.validation-summary-valid {
    display: none;
}

6.3. اعتبارسنجی سمت کلاینت

6.3.1. مبانی jQuery و جاوااسکریپت

اعتبارسنجی سمت کلاینت با استفاده از جاوااسکریپت انجام می‌شود. ما از فریم‌ورک jQuery استفاده خواهیم کرد که توابع متعددی را برای تسهیل توسعه جاوااسکریپت فراهم می‌کند. ما اصول اولیه jQuery را که برای درک اسکریپت‌های این فصل و فصل‌های بعدی باید بدانید، تشریح خواهیم کرد.

یک فایل استاتیک HTML [JQuery-01.html] ایجاد می‌کنیم که آن را در پوشه‌ای به نام [static / vues] قرار می‌دهیم:

 

این فایل شامل موارد زیر خواهد بود:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>JQuery-01</title>
  <script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
</head>
<body>
  <h3>Rudiments de JQuery</h3>
  <div id="element1">
    Elément 1
  </div>
</body>
</html>
  • خط ۶: وارد کردن jQuery;
  • خطوط ۱۰–۱۲: یک عنصر از صفحه با شناسه [element1]. ما قصد داریم با این عنصر آزمایش کنیم.

ما باید فایل [jquery-1.11.1.min.js] را دانلود کنیم. می‌توانیم آخرین نسخه jQuery را در URL [http://jquery.com/download/] پیدا کنیم:

Image

فایل دانلود شده را در پوشه [static / js] قرار دهید:

  

پس از انجام این کار، نمای ایستا [jQuery-01.html] را با استفاده از Chrome [1-2] درخواست کنید:

در گوگل کروم، عبارت [Ctrl-Maj-I] را وارد کنید تا ابزارهای توسعه‌دهنده (developer tools) [3] باز شود. زبانه [Console] [4] به شما امکان می‌دهد کد جاوااسکریپت را اجرا کنید. در زیر، چند دستور جاوااسکریپت برای تایپ کردن به همراه توضیح هر یک ارائه شده است.

JS
نتیجه
$("#element1")
: مجموعه تمام عناصری را که دارای شناسه [element1] هستند بازمی‌گرداند؛ این مجموعه معمولاً شامل ۰ یا ۱ عنصر است، زیرا هیچ دو عناصری در یک صفحه نمی‌توانند شناسه یکسانی داشته باشند (HTML).
$("#element1").text("blabla")
: متن [blabla] را به همه عناصر در مجموعه اعمال می‌کند. این کار محتوای نمایش‌داده‌شده توسط صفحه را تغییر می‌دهد
$("#element1").hide()
عناصر موجود در مجموعه را مخفی می‌کند. متن [blabla] دیگر نمایش داده نمی‌شود.
$("#element1")
: مجموعه را دوباره نمایش می‌دهد. این به ما امکان می‌دهد ببینیم که عنصر با شناسه [element1] دارای ویژگی CSS style='display: none;' است که باعث پنهان شدن عنصر می‌شود.
$("#element1").show()
: آیتم‌های مجموعه را نمایش می‌دهد. متن [blabla] دوباره ظاهر می‌شود. این ویژگی CSS style='display: block;' است که این نمایش را تضمین می‌کند.
$("#element1").attr('style','color: red')
: یک ویژگی را برای همه عناصر در مجموعه تنظیم می‌کند. ویژگی در اینجا [style] است و مقدار آن [color: red] می‌باشد. متن [blabla] به رنگ قرمز درمی‌آید.
Tableau
Dictionnaire

توجه کنید که URL مرورگر در طول این عملیات‌ها تغییر نکرده است. هیچ ارتباطی با سرور وب برقرار نشده است. همه چیز در داخل مرورگر اتفاق می‌افتد. اکنون، بیایید کد منبع صفحه را مشاهده کنیم:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>JQuery-01</title>
  <script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
</head>
<body>
  <h3>Rudiments de JQuery</h3>
  <div id="element1">
    Elément 1
  </div>
</body>
</html>

این متن اصلی است. این متن تغییرات اعمال‌شده بر عنصر در خطوط ۱۰ تا ۱۲ را منعکس نمی‌کند. هنگام اشکال‌زدایی جاوا اسکریپت، مهم است که این موضوع را در نظر داشته باشید. بنابراین، مشاهده کد منبع صفحه نمایش‌داده‌شده اغلب بی‌فایده است.

اکنون به اندازه کافی می‌دانیم تا اسکریپت‌های jS را که در ادامه می‌آیند، درک کنیم.

6.3.2. کتابخانه‌های اعتبارسنجی jS

ما قصد داریم از کتابخانه‌های اکوسیستم jQuery استفاده کنیم. این اکوسیستم که حول jQuery متمرکز است، شامل پروژه‌های متعددی است که خود منجر به ایجاد کتابخانه‌ها می‌شوند. ما از کتابخانه اعتبارسنجی [jquery.validate.unobstrusive] که توسط مایکروسافت ایجاد و به بنیاد jQuery اهدا شده است، استفاده خواهیم کرد. از این پس آن را «کتابخانه اعتبارسنجی MS» یا به طور ساده‌تر «کتابخانه MS» می‌نامیم. برای تهیه آن، به محیط Microsoft Visual Studio نیاز دارید. من راه دیگری برای تهیه آن ندیده‌ام. می‌توان از نسخه‌ای رایگان مانند [Visual Studio Community] یا [http://www.visualstudio.com/en-us/news/vs2013-community-vs.aspx] (دسامبر ۲۰۱۴) استفاده کرد. خوانندگانی که علاقه‌ای به دنبال کردن مراحل زیر ندارند، می‌توانند این کتابخانه و کتابخانه‌های وابسته‌اش را از نمونه‌های ارائه‌شده در وب‌سایت این سند دانلود کنند.

با استفاده از [1-4] یک پروژه کنسول در ویژوال استودیو ایجاد کنید:

12
34
  • در [5]، پروژه کنسول؛
  • در [6-7]: ما بسته‌های [NuGet] را به پروژه اضافه خواهیم کرد. [NuGet] یک قابلیت Visual Studio است که به شما امکان می‌دهد کتابخانه‌ها را به صورت DLL و همچنین کتابخانه‌های jS دانلود کنید.
  • برای [9-10]، با استفاده از کلمه کلیدی [jQuery] جستجو کنید؛
  • برای [11-13]، کتابخانه‌های مورد نیاز برای اعتبارسنجی سمت کلاینت را به ترتیبی که مشخص شده دانلود کنید؛
  • برای [14]، کتابخانه [Microsoft jQuery Unobtrusive Ajax] را نیز دانلود کنید که به زودی از آن استفاده خواهیم کرد؛
  • در [15-16]، با استفاده از کلیدواژه [globalize]، بسته‌ها را جستجو کنید؛
  • برای [17]، کتابخانه [jQuery.Validation.Globalize] را دانلود کنید؛

این دانلودهای مختلف تعدادی کتابخانه jS را در پوشه [Scripts] پروژه [18] نصب کرده‌اند. همهٔ آن‌ها مفید نیستند. هر فایل در دو نسخه عرضه می‌شود:

  • [js]: نسخه قابل‌خواندن کتابخانه؛
  • [min.js]: نسخه غیرقابل‌خواندن و «کوچک‌شده» کتابخانه. در واقع غیرقابل‌خواندن نیست؛ یک متن است. اما قابل فهم نیست. این نسخه‌ای است که باید در تولید استفاده شود، زیرا این فایل کوچکتر از نسخه متناظر [js] است و در نتیجه سرعت ارتباط کلاینت/سرور را بهبود می‌بخشد؛

نسخه‌های [min.map] ضروری نیستند. در پوشه [cultures]، می‌توانید تنها زبان‌هایی را که توسط برنامه مدیریت می‌شوند، حفظ کنید.

با استفاده از اکتشافگر ویندوز (Windows Explorer)، این فایل‌ها را به پوشه [static / js / jquery] در داخل پروژه [springmvc-validation-client] کپی کنید و فقط فایل‌های مرتبط را در [20] حفظ کنید:

در [21]، تنها دو لوکال را نگه دارید:

  • [fr-FR]: فرانسوی (فرانسه)؛
  • [en-US]: انگلیسی از USA;

6.3.3. وارد کردن کتابخانه‌های اعتبارسنجی jS

برای استفاده، این کتابخانه‌ها باید از طریق view [vue-01.xml] وارد شوند:


<head>
        <title>Spring 4 MVC</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="/css/form01.css" />
        <script type="text/javascript" src="/js/jquery/jquery-2.1.1.min.js"></script>
        <script type="text/javascript" src="/js/jquery/jquery.validate.min.js"></script>
        <script type="text/javascript" src="/js/jquery/jquery.validate.unobtrusive.min.js"></script>
        <script type="text/javascript" src="/js/jquery/globalize/globalize.js"></script>
        <script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.fr-FR.js"></script>
        <script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.en-US.js"></script>
        <script type="text/javascript" src="/js/client-validation.js"></script>
        <script type="text/javascript" src="/js/local.js"></script>
        <script th:inline="javascript">
            /*<![CDATA[*/
                    var culture = [[${locale}]];
                    Globalize.culture(culture);
                    /*]]>*/
        </script>
    </head>
  • خط ۱۱: وارد کردن یک فایل jS که هنوز در مورد آن بحث نکرده‌ایم؛
  • خطوط ۱۳–۱۸: یک اسکریپت jS که توسط Thymelaf تفسیر می‌شود. این اسکریپت مدیریت لوکال را در سمت کلاینت بر عهده دارد؛

6.3.4. مدیریت لوکال در سمت کلاینت

محلی‌سازی سمت کلاینت توسط اسکریپت زیر jS انجام می‌شود:


<script th:inline="javascript">
            /*<![CDATA[*/
                    var culture = [[${locale}]];
                    Globalize.culture(culture);
                    /*]]>*/
</script>
  • خطوط ۳–۴: کد jS حاوی عبارت Thymeleaf [[${locale}]]. به نحویات خاص این عبارت توجه کنید. این به این دلیل است که به زبان جاوااسکریپت نوشته شده است. عبارت [[${locale}]] در قالب ویو با مقدار کلید [locale] جایگزین خواهد شد؛

نتیجه در جریان HTML تولید شده از این خطوط به شرح زیر است:


<script>
            /*<![CDATA[*/
                    var culture = 'en-US';
                    Globalize.culture(culture);
                    /*]]>*/
</script>

خطوط ۳–۴ محل (locale) سمت کلاینت را تنظیم می‌کنند. ما تنها دو مورد از آن‌ها را مدیریت می‌کنیم: [fr-FR] و [en-US]. به همین دلیل تنها دو فایل محل را وارد کرده‌ایم:


        <script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="/js/jquery/globalize/cultures/globalize.culture.en-US.js"></script>

محلی که باید در سمت کلاینت استفاده شود، در سمت سرور تنظیم می‌شود. بیایید دوباره به کد سمت سرور نگاه کنیم:


    @RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
    public String js01(Form01 formulaire, Locale locale, Model model) {
        setModel(formulaire, model, locale, null);
        return "vue-01";
    }

    //آماده‌سازی قالب نما vue-01
    private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
        // فقط زبان‌های فرانسوی (fr-FR) و انگلیسی (en-US) پشتیبانی می‌شوند
        String language = locale.getLanguage();
        String country = null;
        if (language.equals("fr")) {
            country = "FR";
            formulaire.setLang("fr_FR");
        }
        if (language.equals("en")) {
            country = "US";
            formulaire.setLang("en_US");
        }
        model.addAttribute("locale", String.format("%s-%s", language, country));
...
}
  • خط ۲۰: لوکال [fr-FR] یا [en-US] در قالب نما [vue-01.xml] (خط ۴) تنظیم شده است. شایان ذکر است که منبعی از پیچیدگی‌ها وجود دارد. در حالی که لوکال فرانسوی در سمت کلاینت با [fr-FR] مشخص می‌شود، در سمت سرور با [fr_FR] مشخص می‌شود. به همین دلیل، در خطوط 14 و 18، این مقدار در شیء [Form01 formulaire] که مقادیر ارسال‌شده را دریافت می‌کند، به این شکل ذخیره می‌شود؛

شایان ذکر است نکته مهم زیر. اسکریپت


<script>
            /*<![CDATA[*/
                    var culture = 'en-US';
                    Globalize.culture(culture);
                    /*]]>*/
</script>

محلی‌سازی کلاینت را بر اساس محلی که توسط سرور ارائه می‌شود تغییر می‌دهد. این کار پیام‌های نمایش‌داده‌شده در صفحه را بین‌المللی نمی‌کند؛ بلکه صرفاً نحوه تفسیر برخی اطلاعات خاص هر کشور را تغییر می‌دهد. با فرهنگ [fr_FR]، عدد واقعی [12,78] معتبر است، در حالی که با فرهنگ [en-US] نامعتبر است. بنابراین باید [12.78] را بنویسید. به همین ترتیب، تاریخ [12/01/2014] در محله [fr-FR] یک تاریخ معتبر است، در حالی که در محله [en-US] باید به صورت [01/12/2014] نوشته شود. فایل‌های موجود در پوشه [jquery / globalize] این نوع مسائل را مدیریت می‌کنند:

  

بین‌المللی‌سازی پیام‌های خطا صرفاً در سمت سرور انجام می‌شود. خواهیم دید که صفحه HTML / jS پیام‌های خطای مربوط به لوکِیلی را که توسط سرور مدیریت می‌شود، همراه دارد: برای زبان فرانسوی در لوکال [fr_FR] و برای زبان انگلیسی در لوکال [en_US].

6.3.5. فایل‌های پیام

ویوی [vue-01.xml] از پیام‌های بین‌المللی‌سازی‌شده زیر استفاده می‌کند:

  

[messages_fr.properties]


NotNull=Le champ est obligatoire
NotEmpty=La donnée ne peut être vide
NotBlank=La donnée ne peut être vide
typeMismatch=Format invalide
Future.form01.dateInFuture=La date doit être postérieure ou égale à celle d''aujourd'hui
Past.form01.dateInPast=La date doit être antérieure ou égale à celle d''aujourd'hui
Min.form01.intMin10=La valeur doit être supérieure ou égale à 10
Max.form01.intMax100=La valeur doit être inférieure ou égale à 100
Size.form01.strBetween4and6=La chaîne doit avoir entre 4 et 6 caractères
Length.form01.str4=La chaîne doit avoir quatre caractères exactement
Email.form01.email=Adresse mail invalide
URL.form01.url=URL invalide
Range.form01.int1014=La valeur doit être dans l''intervalle [10,14]
AssertTrue=Seule la valeur True est acceptée
AssertFalse=Seule la valeur False est acceptée
Pattern.form01.hhmmss=Tapez l''heure sous la forme hh:mm:ss
form01.hhmmss.pattern=^\\d{2}:\\d{2}:\\d{2}$
DateInvalide.form01=Date invalide
form01.str4.pattern=^.{4,4}$
form01.int1014.max=14
form01.int1014.min=10
form01.strBetween4and6.pattern=^.{4,6}$
form01.intMax100.value=100
form01.intMin10.value=10
form01.double1.min=2.3
form01.double1.max=3.4
Range.form01.double1=La valeur doit être dans l'intervalle [2,3-3,4]
form01.title=Formulaire - Validations côté client - locale=
form01.col1=Contrainte
form01.col2=Saisie
form01.col3=Validation client
form01.col4=Validation serveur
form01.valider=Valider
form01.double2=[double2+double1] doit être dans l''intervalle [{0},{1}]
form01.double3=[double3+double1] doit être dans l''intervalle [{0},{1}]
locale.fr=Français
locale.en=English
client.validation.true=Activer la validation client
client.validation.false=Inhiber la validation client
DecimalMin.form01.double1=Le nombre doit être supérieur ou égal à 2,3
DecimalMax.form01.double1=Le nombre doit être inférieur ou égal à 3,4
server.error.message=Erreurs détectées par les validateurs côté serveur

[messages_en.properties]


NotNull=Field is required
NotEmpty=Field can''t be empty
NotBlank=Field can''t be empty
typeMismatch=Invalid format
Future.form01.dateInFuture=Date must be greater or equal to today''s date
Past.form01.dateInPast=Date must be lower or equal today''s date
Min.form01.intMin10=Value must be higher or equal to 10
Max.form01.intMax100=Value must be lower or equal to 100
Size.form01.strBetween4and6=String must have between 4 and 6 characters
Length.form01.str4=String must be exactly 4 characters long
Email.form01.email=Invalid mail address
URL.form01.url=Invalid URL
Range.form01.int1014=Value must be in [10,14]
AssertTrue=Only value True is allowed
AssertFalse=Only value False is allowed
Pattern.form01.hhmmss=Time must follow the format hh:mm:ss
form01.hhmmss.pattern=^\\d{2}:\\d{2}:\\d{2}$
DateInvalide.form01=Invalid Date
form01.str4.pattern=^.{4,4}$
form01.int1014.max=14
form01.int1014.min=10
form01.strBetween4and6.pattern=^.{4,6}$
form01.intMax100.value=100
form01.intMin10.value=10
form01.double1.min=2.3
form01.double1.max=3.4
Range.form01.double1=Value must be in [2.3,3.4]
form01.title=Form - Client side validation - locale=
form01.col1=Constraint
form01.col2=Input
form01.col3=Client validation
form01.col4=Server validation
form01.valider=Validate
form01.double2=[double2+double1] must be in [{0},{1}]
form01.double3=[double3+double1] must be in [{0},{1}]
locale.fr=Français
locale.en=English
client.validation.true=Activate client validation
client.validation.false=Inhibate client validation
DecimalMin.form01.double1=Value must be greater or equal to 2.3
DecimalMax.form01.double1=Value must be lower or equal to 3.4
server.error.message=Errors detected by the validators on the server side

فایل [messages.properties] کپی فایل پیام انگلیسی است. در نتیجه، هر لوکالی غیر از [fr] از پیام‌های انگلیسی استفاده خواهد کرد. لطفاً توجه داشته باشید که فایل [messages_fr.properties] برای هر لوکال به شکل [fr_XX]، مانند [fr_CA] یا [fr_FR]، استفاده می‌شود.

نما [vue-01.xml] از کلیدهای این پیام‌ها استفاده می‌کند. اگر خواننده بخواهد مقدار مرتبط با این کلیدها را بداند، دعوت می‌شود به این پاراگراف بازگردد تا آن را بیابد.

6.3.6. تغییر لوکال

ویوی [vue-01.xml] شامل چهار لینک است:


<body>
        <!-- عنوان -->
        <h3>
            <span th:text="#{form01.title}"></span>
            <span th:text="${locale}"></span>
        </h3>
        <!-- منو -->
        <p>
            <a id="locale_fr" href="javascript:setLocale('fr_FR')">
                <span th:text="#{locale.fr}"></span>
            </a>
            <a id="locale_en" href="javascript:setLocale('en_US')">
                <span style="margin-left:30px" th:text="#{locale.en}"></span>
            </a>
            <a id="clientValidationTrue" href="javascript:setClientValidation(true)">
                <span style="margin-left:30px" th:text="#{client.validation.true}"></span>
            </a>
            <a id="clientValidationFalse" href="javascript:setClientValidation(false)">
                <span style="margin-left:30px" th:text="#{client.validation.false}"></span>
            </a>
        </p>
        <!-- فرم -->
        <form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
            ...

که برخی از آن‌ها در زیر نشان داده شده است [1]:

بیایید به دو پیوندی که به شما امکان می‌دهند زبان را به فرانسوی یا انگلیسی تغییر دهید، نگاهی بیندازیم:


            <a id="locale_fr" href="javascript:setLocale('fr_FR')">
                <span th:text="#{locale.fr}"></span>
            </a>
            <a id="locale_en" href="javascript:setLocale('en_US')">
                <span style="margin-left:30px" th:text="#{locale.en}"></span>
</a>

کلیک روی این لینک‌ها باعث اجرای یک اسکریپت به نام jS می‌شود که در فایل [local.js] [2] قرار دارد. در هر دو مورد، تابع jS [setLocale] فراخوانی می‌شود:


// محلی
function setLocale(locale) {
    // محلی به‌روزرسانی می‌شود
    lang.val(locale);
    //ارسال فرم – این کار اعتبارسنج‌های سمت کلاینت را فعال نمی‌کند – به همین دلیل اعتبارسنجی سمت کلاینت غیرفعال نشده است
    document.form.submit();
}

برای درک خط ۴، مقداری پیش‌زمینه لازم است. نما [vue-01.xml] شامل یک فیلد مخفی به نام [lang] است:


<input type="hidden" th:field="*{lang}" th:value="*{lang}" value="true" />

که با فیلدی به نام [lang] در [Form01] مطابقت دارد:


    // محلی
    private String lang;

فیلدهای مخفی زمانی مفید هستند که بخواهید مقادیر ارسال‌شده را غنی‌سازی کنید. جاوااسکریپت به شما امکان می‌دهد برای آن‌ها مقداری تعیین کنید و این مقدار مانند یک ورودی عادی کاربر ارسال می‌شود. کد HTML تولیدشده توسط Thymeleaf به شرح زیر است:


<input type="hidden" value="en_US" id="lang" name="lang" />

مقدار پارامتر [value]، مقدار فیلد [Form01.lang] در زمانی است که HTML تولید شده است. توجه به شناسه jS گره [id="lang"] مهم است. این شناسه توسط تابع زیر [] استفاده می‌شود:


// متغیرهای سراسری
var lang;

//آماده شدن سند
$(document).ready(function() {
    // ارجاعات سراسری
    lang = $("#lang");
});

// محلی
function setLocale(locale) {
    // محلی به‌روزرسانی می‌شود
    lang.val(locale);
    //فرم ارسال شده است – به دلایلی نامعلوم، این باعث فعال شدن اعتبارسنج‌های سمت کلاینت نمی‌شود
    // به همین دلیل اعتبارسنجی غیرفعال نشده است
    document.form.submit();
}
  • خطوط ۵–۸: تابع jS [$(document).ready(f)] تابعی است که پس از بارگذاری کامل سند ارسال‌شده توسط سرور توسط مرورگر اجرا می‌شود. پارامتر آن یک تابع است. تابع jS [$(document).ready(f)] برای inicialise کردن محیط jS سند بارگذاری شده استفاده می‌شود؛
  • خط ۷: عبارت [$("#lang")] یک عبارت jQuery است. مقدار آن یک مرجع به گره DOM با ویژگی [id='lang'] است؛
  • خط ۲: متغیرهایی که خارج از یک تابع اعلام می‌شوند، برای همه توابع سراسری (global) هستند. در اینجا، این بدان معناست که متغیر [lang] که در [$(document).ready()] مقداردهی اولیه شده است، در داخل تابع [setLocale] در خط ۱۱ نیز قابل دسترسی است؛
  • خط ۱۳: ویژگی [value] را در گره با شناسه [lang] تغییر می‌دهد. اگر lang برابر با [xx_XX] باشد، آنگاه تگ HTML گره به صورت زیر در می‌آید:

<input type="hidden" value="xx_XX" id="lang" name="lang" />

جاوااسکریپت به شما امکان می‌دهد مقادیر عناصر در DOM (مدل شیء مستند) را تغییر دهید.

  • خط ۱۶: [document] به DOM اشاره می‌کند. [document.form] به اولین فرم یافت‌شده در این سند اشاره می‌کند. یک سند HTML ممکن است حاوی چندین تگ <form> و در نتیجه چندین فرم باشد. در اینجا، ما فقط یک فرم داریم. [document.form.submit] این فرم را طوری ارسال می‌کند که گویی کاربر دکمه‌ای با ویژگی [type='submit'] را کلیک کرده است. مقادیر فرم به کدام اکشن ارسال می‌شوند؟ برای پی بردن به این موضوع، به تگ [form] مربوط به فرم در داخل [vue-01.xml] نگاه کنید:

        <!--فرم -->
        <form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">

اقدامی که مقادیر ارسال‌شده را دریافت می‌کند، همان اقدامی است که توسط ویژگی [th:action] مشخص شده است. بنابراین، این اقدام [/js02.html] خواهد بود. توجه داشته باشید که در این نام، پسوند [.html] حذف خواهد شد و در نهایت، اکشن [/js02] اجرا می‌شود. مهم است که درک کنیم مقدار جدید [xx_XX] برای گره [lang] در فرم [lang=xx_XX] ارسال خواهد شد. با این حال، ما برنامه‌مان را طوری پیکربندی کرده‌ایم که پارامتر [lang] را رهگیری کرده و آن را به‌عنوان تغییر locale تفسیر کند. بنابراین، در سمت سرور، locale به [xx_XX] تبدیل خواهد شد. بیایید نگاهی بیندازیم به اکشن [/js02] که قرار است اجرا شود:


    @RequestMapping(value = "/js02", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
    public String js02(@Valid Form01 formulaire, BindingResult result, RedirectAttributes redirectAttributes, Locale locale, Model model) {
        Form01Validator validator = new Form01Validator(10, 13);
        validator.validate(formulaire, result);
        if (result.hasErrors()) {
            StringBuffer buffer = new StringBuffer();
            for (ObjectError error : result.getAllErrors()) {
                buffer.append(String.format("[name=%s,code=%s,message=%s]", error.getObjectName(), error.getCode(),
                        error.getDefaultMessage()));
            }
            setModel(formulaire, model, locale, buffer.toString());
            return "vue-01";
        } else {
            redirectAttributes.addFlashAttribute("form01", formulaire);
            return "redirect:/js01.html";
        }
    }

    // آماده‌سازی قالب نما برای view-01
    private void setModel(Form01 formulaire, Model model, Locale locale, String message) {
        // فقط از زبان‌های فرانسوی (fr-FR) و انگلیسی (en-US) پشتیبانی می‌کند
        String language = locale.getLanguage();
        String country = null;
        if (language.equals("fr")) {
            country = "FR";
            formulaire.setLang("fr_FR");
        }
        if (language.equals("en")) {
            country = "US";
            formulaire.setLang("en_US");
        }
        model.addAttribute("locale", String.format("%s-%s", language, country));
        ...
}
  • خط ۲: اکشن [/js02]، لوکال جدید [xx_XX] را که در پارامتر [Locale locale] جای داده شده است، دریافت خواهد کرد:
  • خطوط ۵–۱۲: اگر هر یک از مقادیر ارسال‌شده نامعتبر باشند، نما [vue-01.xml] با پیام‌های خطا و با استفاده از لوکال جدید [xx_XX] نمایش داده خواهد شد. علاوه بر این، خط ۱۱ تضمین می‌کند که متغیر [locale=xx-XX] در قالب درج شود. در سمت کلاینت، این مقدار برای به‌روزرسانی محیط بومی سمت کلاینت استفاده خواهد شد. ما این فرآیند را شرح داده‌ایم؛
  • خطوط ۱۴–۱۵: اگر همه مقادیر ارسال‌شده معتبر باشند، آنگاه به اقدام بعدی، [/js01]، هدایت می‌شود:

    @RequestMapping(value = "/js01", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
    public String js01(Form01 formulaire, Locale locale, Model model) {
        setModel(formulaire, model, locale, null);
        return "vue-01";
}
  • خط ۲: لوکال جدید [xx_XX] تزریق می‌شود؛
  • خط ۳: متد [setModel] سپس لوکال کلاینت را روی [xx-XX] تنظیم می‌کند؛

اکنون بیایید به تأثیر لوکال در نمای [vue-01.xml] نگاهی بیندازیم. ما هنوز آن را به طور کامل نشان نداده‌ایم، زیرا بیش از ۳۰۰ خط دارد. با این حال، بیشتر خطوط شامل تکرار دنباله‌ای مشابه موارد زیر است:


<!--الزامی -->
<tr>
  <td class="col1">required</td>
  <td class="col2">
    <input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
  </td>
    <td class="col3">
      <span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
    </td>
  <td class="col4">
      <span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
  </td>
</tr>

این کد قطعه زیر را نمایش می‌دهد: [1]:

پیام خطای [2] از ویژگی [th:attr="data-val-required=#{NotNull}"] در خط ۵ نشأت می‌گیرد. [#{NotNull}] یک پیام بومی‌شده است. بسته به بومی‌سازی سمت سرور، خط ۵ تگ را تولید می‌کند:


<input type="text" data-val="true" data-val-required="Field is required" id="strNotEmpty" name="strNotEmpty" />

یا تگ:


<input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" />

ویژگی‌های [data-x] توسط کتابخانه اعتبارسنجی jS پردازش می‌شوند.

در نهایت باید توجه داشت که دو لینک برای تغییر لوکال:

  • راه‌اندازی اعتبارسنجی POST برای مقادیر واردشده؛
  • محلی‌سازی را هم در سمت سرور و هم در سمت کلاینت تغییر می‌دهد؛
  • یک صفحه HTML تولید می‌کند که پیام‌های خطا را برای کتابخانه اعتبارسنجی jS همراه دارد، و این پیام‌ها به زبان منطقه انتخاب‌شده هستند؛

6.3.7. POST مقادیر واردشده

بیایید دکمه [Valider] را بررسی کنیم که مقادیر واردشده در نمای [vue-01.xml] را ارسال می‌کند. کد آن، HTML، به شرح زیر است:


<!-- دکمه ارسال -->
<input type="submit" value="Valider" onclick="javascript:postForm01()" />

اگر جاوا اسکریپت در مرورگر فعال باشد، کلیک کردن روی دکمه باعث اجرای متد [postForm01] خواهد شد. اگر این تابع مقدار بوله‌ای [False] را برگرداند، آنگاه submit اجرا نخواهد شد. اگر مقدار دیگری برگرداند، آنگاه اجرا خواهد شد. این تابع در فایل [local.js] قرار دارد:

 

این تابع توسط ویو [vue-01.xml] از طریق خط ۶ زیر وارد شده است:


    <head>
        <title>Spring 4 MVC</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="/css/form01.css" />
...
        <script type="text/javascript" src="/js/local.js"></script>
</head>

در این فایل، کد زیر قابل مشاهده است:


// متغیرهای سراسری
var formulaire;
var clientValidation;
var double1;
var double2;
var double3;
...
$(document).ready(function() {
    // ارجاعات سراسری
    formulaire = $("#form");
    clientValidation = $("#clientValidation");
    double1 = $("#double1");
    double2 = $("#double2");
    double3 = $("#double3");
...
});
....
//ارسال فرم
function postForm01() {
...
}
  • خطوط ۸–۱۶: تابع jS [$(document).ready(f)] تابعی است که پس از بارگذاری کامل سند ارسالی از سمت سرور توسط مرورگر، اجرا می‌شود. پارامتر آن یک تابع است. تابع jS [$(document).ready(f)] برای راه‌اندازی محیط jS سند بارگذاری‌شده استفاده می‌شود؛
  • خطوط ۱۰–۱۴: برای درک این خطوط، باید هم کد Thymeleaf و هم کد تولیدشده HTML را بررسی کنید؛

کد مرتبط Thymeleaf به شرح زیر است:


<form action="/someURL" th:action="@{/js02.html}" method="post" th:object="${form01}" name="form" id="form">
...
<input type="text" th:field="*{double1}" th:value="*{double1}" ... />
...
<input type="text" th:field="*{double2}" th:value="*{double2}" />
...
<input type="text" th:field="*{double3}" th:value="*{double3}" ... />
...
<input type="hidden" th:field="*{clientValidation}" th:value="*{clientValidation}" value="true" />

که کد HTML زیر را تولید می‌کند:


<form action="/js02.html" method="post" name="form" id="form">
...
<input type="text" id="double1" name="double1" .../>
....
<input type="text" value="" id="double2" name="double2" />
...
<input value="" id="double3" name="double3" .../>
...
<input type="hidden" value="false" id="clientValidation" name="clientValidation" /> 

هر ویژگی [th:field='x'] دو ویژگی تولید می‌کند: HTML، [name='x'] و [id='x']. ویژگی [name] نام مقادیر ارسال‌شده است. بنابراین، وجود ویژگی‌های [name='x'] و [value='y'] برای یک HTML <input type='text'> رشته x=y را در مقادیر ارسال‌شده name1=val1&name2=val2&... درج خواهد کرد. ویژگی [id='x'] توسط جاوااسکریپت استفاده می‌شود. این ویژگی برای شناسایی یک عنصر از DOM (مدل شیء مستند) به کار می‌رود. سند بارگذاری‌شده HTML در واقع به یک درخت جاوااسکریپت به نام DOM تبدیل می‌شود، جایی که هر گره با ویژگی [id] خود شناسایی می‌شود.

بیایید به کد تابع [$(document).ready()] بازگردیم:


// متغیرهای سراسری
var formulaire;
var clientValidation;
var double1;
var double2;
var double3;
...
$(document).ready(function() {
    // ارجاعات سراسری
    formulaire = $("#form");
    clientValidation = $("#clientValidation");
    double1 = $("#double1");
    double2 = $("#double2");
    double3 = $("#double3");
...
});
....
//ارسال فرم
function postForm01() {
...
}
  • خط ۱۰: عبارت [$("#form")] یک عبارت jQuery است. مقدار آن ارجاعی به گره DOM با ویژگی [id='form '] است؛
  • خطوط ۱۰–۱۴: ارجاعات به پنج گره از DOM بازیابی می‌شوند؛
  • خطوط ۲–۶: متغیرهایی که خارج از یک تابع تعریف می‌شوند، برای همه توابع سراسری (global) هستند. در اینجا، این بدان معناست که متغیرهای [formulaire, clientValidation , double1, double2, double3] که در [$(document).ready()] مقداردهی اولیه شده‌اند، در تابع [postForm01] در خط ۱۹ نیز قابل دسترسی خواهند بود؛

اکنون، بیایید به تابع [postForm01] نگاه کنیم:


//ارسال فرم
function postForm01() {
    // حالت اعتبارسنجی سمت کلاینت
    var validationActive = clientValidation.val() === "true";
    if (validationActive) {
        // پاک کردن خطاهای سرور
        clearServerErrors();
        //اعتبارسنجی فرم
        if (!formulaire.validate().form()) {
            //ارسال نشد
            return false;
        }
    }
    // مقادیر عددی در قالب انگلو-ساکسون
    var value1 = double1.val().replace(",", ".");
    double1.val(value1);
    var value2 = double2.val().replace(",", ".");
    double2.val(value2);
    var value3 = double3.val().replace(",", ".");
    double3.val(value3);
    // اجازه ارسال فرم
    return true;
}

به یاد داشته باشید که این تابع، jS، قبل از [submit] فرم اجرا می‌شود. اگر مقدار بولین [false] (خط ۱۱) را بازگرداند، فرم ارسال نخواهد شد. اگر مقدار دیگری بازگرداند (خط ۲۲)، فرم ارسال خواهد شد.

  • کد کلیدی در خطوط ۴ تا ۱۲ قرار دارد؛
  • خط ۴: مقدار فیلد پنهان [clientValidation] را بازیابی می‌کنیم. این مقدار در صورتی که اعتبارسنجی سمت کلاینت باید فعال باشد، 'true' و در غیر این صورت 'false' است؛
  • خط ۶: در صورت اعتبارسنجی سمت کلاینت، هرگونه پیام خطای سرور را که ممکن است وجود داشته باشد پاک می‌کنیم، زیرا کاربر به تازگی زبان و منطقه زمانی خود را تغییر داده است؛
  • خط ۹: به یاد داشته باشید که متغیر [formulaire] نماینده گره تگ <form> از نوع HTML است، یعنی خود فرم. این فرم شامل اعتبارسنج‌های jS است که هنوز در مورد آن‌ها بحث نکرده‌ایم و در پاراگراف‌های بعدی به آن‌ها خواهیم پرداخت. بیان [formulaire.validate().form()] اجرای تمام اعتبارسنج‌های jS موجود در فرم را اجباری می‌کند. مقدار آن در صورتی که همه مقادیر بررسی‌شده معتبر باشند [true] و در غیر این صورت [false] است؛
  • خط ۱۱: مقدار [false] بازگردانده می‌شود اگر حداقل یکی از مقادیر بررسی‌شده نامعتبر باشد. این کار مانع از ارسال [submit] از فرم به سرور می‌شود؛
  • خطوط ۱۵–۲۰: شناسه‌های [double1, double2, double3] نمایانگر سه عدد واقعی از فرم هستند. بسته به لوکال، مقدار وارد شده متفاوت است. با لوکال [fr-FR]، مقدار وارد شده [10,37] است، در حالی که با لوکال [en-US]، مقدار وارد شده [10.37] است. این برای ورود داده است. با فرهنگ [fr-FR]، مقداری که برای [double1] ارسال می‌شود، شبیه [double1=10,37] خواهد بود. به محض اینکه به سرور برسد، مقدار [10,37] رد خواهد شد زیرا سرور انتظار [10.37]، فرمت پیش‌فرض برای اعداد ممیز شناور در جاوا را دارد. بنابراین، در خطوط ۱۵ تا ۲۰، ویرگول در مقدار وارد شده برای این اعداد با یک نقطه جایگزین می‌شود؛
  • خط ۱۵: عبارت [double1.val()] رشتهٔ کاراکتری واردشده برای گره [double1] را برمی‌گرداند. عبارت [double1.val().replace(",", ".")] ویرگول‌های این رشته را با نقطه‌ها جایگزین می‌کند. نتیجه عبارت [value1] است؛
  • خط ۱۶: دستور [double1.val(value1)] این مقدار، [value1]، را به گره [double1] اختصاص می‌دهد.

از نظر فنی، اگر کاربر برای مقدار واقعی [double1]، مقدار [10,37] را وارد کرده باشد، طبق دستورالعمل‌های قبلی، گره [double1] مقدار [10.37] را خواهد داشت، و مقداری که ارسال می‌شود [param1=val1&double1=10.37&param2=val2] خواهد بود که توسط سرور پذیرفته می‌شود؛

  • خط ۲۲: مقدار [true] بازگردانده می‌شود تا [submit] در فرم اجرا شود؛

توجه داشته باشید که تابع jS [postForm01]:

  • در صورتی که اعتبارسنجی سمت کلاینت فعال باشد، تمام اعتبارسنج‌های jS فرم را اجرا می‌کند، و اگر هر یک از مقادیر وارد شده نامعتبر اعلام شده باشد، از ارسال [submit] فرم به سرور جلوگیری می‌کند؛
  • اجازه می‌دهد تا [submit] اجرا شود، یا به این دلیل که اعتبارسنجی سمت کلاینت فعال نیست، یا به این دلیل که فعال است و تمام مقادیر وارد شده معتبر هستند؛

این امر دستورالعمل در خط [3] را باقی می‌گذارد:


    // پاک کردن خطاهای سرور
clearServerErrors();

هدف تابع [clearServerErrors] پاک کردن پیام‌ها در ستون ۴ از نما [vue-01.xml] است:

در تصویر بالا، ما روی لینک [English] کلیک کردیم. مشاهده کردیم که این کار باعث ارسال مقادیر وارد شده به‌عنوان POST شد، بدون اینکه اعتبارسنج‌های jS فعال شوند. وقتی پاسخ POST بازگردانده می‌شود، ستون [Server Validation] با هرگونه پیام خطا پر می‌شود. اگر اکنون در حالی که اعتبارسنج‌های jS فعال هستند [3]، روی دکمه [Validate] [2] کلیک کنید، سپس ستون [Client Validation] [4] با پیام‌ها پر خواهد شد. اگر اقدامی صورت نگیرد، ورودی‌های ستون [Server Validation] باقی خواهند ماند که باعث سردرگمی می‌شود زیرا در صورت شناسایی خطا توسط اعتبارسنج‌های jS، سرور فراخوانده نمی‌شود. برای جلوگیری از این، ستون [Server Validation] را در تابع [postForm01] پاک می‌کنیم. این تابع [] است که این کار را انجام می‌دهد:


function clearServerErrors() {
    //پاک کردن پیام‌های خطای سرور
    $(".error").each(function(index) {
        $(this).text("");
    });
}

ویژگی متمایز پیام‌های خطا این است که همگی دارای کلاس [error] هستند. برای مثال، برای سطر اول جدول در [vue-01.html]:


<span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>

و این‌ها تنها گره‌هایی در DOM هستند که این کلاس را دارند. ما از این ویژگی در تابع [clearServerErrors] استفاده می‌کنیم:


function clearServerErrors() {
    // پاک کردن پیام‌های خطای سرور
    $(".error").each(function(index) {
        $(this).text("");
    });
}
  • خط ۳: عبارت [$(".error")] مجموعه گره‌هایی را از DOM بازمی‌گرداند که کلاس [error] را دارند؛
  • خط ۳: عبارت [$(".error").each(function(index){f}] تابع [f] را برای هر گره در مجموعه اجرا می‌کند. این تابع یک پارامتر [index] دریافت می‌کند که در اینجا استفاده نمی‌شود و نمایانگر شماره گره در مجموعه است؛
  • خط ۴: عبارت [$(this)] به گره فعلی در حلقه‌ی تکرار اشاره دارد. این یک تگ <span> است. عبارت [$(this).text("")] رشته‌ی خالی را به متن نمایش‌داده‌شده توسط تگ <span> اختصاص می‌دهد؛

اکنون بررسی‌کننده‌های مختلف jS را بررسی خواهیم کرد.

6.3.8. اعتبارسنج [required]

بیایید به اولین عنصر فرم نگاه کنیم:

خط [1] توسط توالی زیر در نمای [vue-01.xml] تولید می‌شود:


<!-- الزامی -->
<tr>
  <td class="col1">required</td>
  <td class="col2">
    <input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />
  </td>
  <td class="col3">
    <span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>
  </td>
  <td class="col4">
    <span th:if="${#fields.hasErrors('strNotEmpty')}" th:errors="*{strNotEmpty}" class="error">Donnée erronée</span>
  </td>
</tr>

این خطوط مربوط به فیلد [strNotEmpty] در فرم [Form01] هستند:


    @NotNull
    @NotBlank
private String strNotEmpty;

محدودیت‌های [1-2] تعیین می‌کنند که فیلد [strNotEmpty] باید یک رشته موجود [NotNull] باشد و نباید خالی باشد یا صرفاً از فاصله‌ها [NotBlank] تشکیل شده باشد. ما می‌خواهیم این محدودیت را در سمت کلاینت با استفاده از جاوااسکریپت شبیه‌سازی کنیم.

بیایید خطوط ۵ و ۸ را بررسی کنیم. خط ۱۱ مشکلی ایجاد نمی‌کند. این خط پیام خطای مربوط به فیلد [strNotEmpty] را نمایش می‌دهد. بیایید با خط ۵ شروع کنیم:


<input type="text" th:field="*{strNotEmpty}" data-val="true" th:attr="data-val-required=#{NotNull}" />

از روی این کد، Thymeleaf تگ زیر را تولید می‌کند:


<input type="text" data-val="true" data-val-required="Field is required" id="strNotEmpty" name="strNotEmpty" value="x" />
  • ویژگی [data-val='true'] توسط کتابخانه‌های اعتبارسنجی jQuery استفاده می‌شود. وجود آن نشان می‌دهد که مقدار گره مشمول اعتبارسنجی است؛
  • ویژگی [data-val-X='msg'] دو مورد اطلاعات را ارائه می‌دهد. [X] نام اعتبارسنج است، در حالی که [msg] پیام خطایی است که با مقدار نامعتبر گره مورد اعمال اعتبارسنج مرتبط است. این تنها برای اطلاع‌رسانی است. این باعث نمایش پیام خطا نمی‌شود؛
  • برچسب‌های [required] یک اعتبارسنج هستند که توسط کتابخانه اعتبارسنجی [jquery.validate.unobstrusive] مایکروسافت شناخته می‌شوند. نیازی به تعریف آن‌ها نیست. این موضوع در آینده همیشه صادق نخواهد بود؛
  • تگ‌های [data-x] توسط HTML5 نادیده گرفته می‌شوند. آنها تنها در صورتی مفید هستند که جاوااسکریپتی برای استفاده از آنها وجود داشته باشد؛

حال به خط ۸ نگاه می‌کنیم:


<span class="field-validation-valid" data-valmsg-for="strNotEmpty" data-valmsg-replace="true"></span>

این برای نمایش پیام خطا از اعتبارسنج [required] استفاده می‌شود. اگر خطایی رخ دهد، کتابخانه اعتبارسنجی jS به‌صورت پویا خط HTML در جدول را با کد زیر جایگزین می‌کند:


<tr>
    <td class="col1">required</td>
    <td class="col2">
        <input type="text" data-val="true" data-val-required="Le champ est obligatoire" id="strNotEmpty" name="strNotEmpty" value="" aria-required="true" aria-invalid="true" aria-describedby="strNotEmpty-error" class="input-validation-error">
    </td>
    <td class="col3">
        <span class="field-validation-error" data-valmsg-for="strNotEmpty" data-valmsg-replace="true">
            <span id="strNotEmpty-error" class="">Le champ est obligatoire</span>
        </span>
    </td>
    <td class="col4">
        <span class="error"></span>
    </td>
</tr>
</tr>
  • خط ۴: کلاس گره [strNotEmpty] تغییر کرده است. به [input-validation-error] تبدیل شده است، که باعث می‌شود فیلد خطا به رنگ قرمز برجسته شود؛
  • خط ۷: کلاس [span] تغییر کرده است. این کلاس به [field-validation-error] تبدیل شده است که باعث می‌شود متن [span] به رنگ قرمز نمایش داده شود؛
  • خط ۸: [span] که قبلاً خالی بود، اکنون حاوی متن [Le champ est obligatoire] است. این متن از تگ [data-val-required="Le champ est obligatoire"] در خط ۴ آمده است؛
  • خط ۷: برای نمایش پیام خطا برای گره [strNotEmpty] در خط ۴، باید از ویژگی‌های [data-valmsg-for="strNotEmpty"] و [data-valmsg-replace="true"] در خط ۷ استفاده شود؛

6.3.9. اعتبارسنج [assertfalse]

خط [1] توسط دنباله زیر از نما [vue-01.xml] تولید می‌شود:


<!-- ضروری، assertfalse -->
<tr>
    <td class="col1">required, assertfalse</td>
    <td class="col2">
        <input type="radio" th:field="*{assertFalse}" value="true" data-val="true"
            th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />
        <label th:for="${#ids.prev('assertFalse')}">true</label>
        <input type="radio" th:field="*{assertFalse}" value="false" data-val="true"
            th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />
        <label th:for="${#ids.prev('assertFalse')}">false</label>
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="assertFalse" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('assertFalse')}" th:errors="*{assertFalse}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [assertFalse] در فرم [Form01] هستند:


    @NotNull
    @AssertFalse
private Boolean assertFalse;

ما می‌خواهیم این محدودیت را در سمت کلاینت با استفاده از جاوااسکریپت شبیه‌سازی کنیم. خطوط ۱۲–۱۷ اکنون استاندارد هستند:

  • خطوط ۱۲–۱۴: در صورت بروز خطا در فیلد [assertFalse]، پیامی را که در ویژگی [data-val-assertfalse] در خط ۶ یا در ویژگی [data-val-required] در همان خط قرار دارد، نمایش دهید. توجه داشته باشید که این پیام‌ها بومی‌سازی شده‌اند، یعنی به زبانی که قبلاً توسط کاربر انتخاب شده یا در صورت عدم انتخاب، به زبان فرانسوی نمایش داده می‌شوند؛
  • خطوط ۵–۱۰: نمایش دکمه‌های رادیویی با اعتبارسنج‌های جاوااسکریپت که به محض کلیک کاربر روی یکی از آن‌ها فعال می‌شوند.

هر دو دکمه به یک روش ساخته شده‌اند. بیایید به اولی نگاه کنیم:


<input type="radio" th:field="*{assertFalse}" value="true" data-val="true" th:attr="data-val-required=#{NotNull},data-val-assertfalse=#{AssertFalse}" />

پس از پردازش توسط Thymeleaf، این خط به شکل زیر درمی‌آید:


<input type="radio" value="true" data-val="true" data-val-required="Le champ est obligatoire" data-val-assertfalse="Seule la valeur False est acceptée" id="assertFalse1" name="assertFalse" />

ما اعتبارسنج‌هایی به نام [data-val="true"] داریم. دو اعتبارسنج وجود دارد: یکی با نام [required] [data-val-required="Le champ est obligatoire"] و دیگری با نام [assertfalse] [data-val-assertfalse="Seule la valeur False est acceptée"]. به یاد داشته باشید که مقدار ویژگی [data-val-X] پیام خطا برای اعتبارسنج X است.

ما قبلاً بررسی‌کننده [required] را بررسی کرده‌ایم. نکته جدید این است که می‌توانیم چندین اعتبارسنج را به یک مقدار واردشده متصل کنیم. در حالی که اعتبارسنج [required] توسط کتابخانه اعتبارسنجی MS (مایکروسافت) شناسایی می‌شود، این امر برای اعتبارسنج [assertFalse] صدق نمی‌کند. بنابراین یاد می‌گیریم چگونه یک اعتبارسنج جدید بسازیم. ما چندین مورد از آن‌ها را ایجاد خواهیم کرد و در فایلی به نام [client-validation.js] قرار داده خواهند شد:

  

این فایل، مانند سایر فایل‌ها، توسط view [vue-01.xml] (خط ۶ زیر) وارد می‌شود:


    <head>
        <title>Spring 4 MVC</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="/css/form01.css" />
        ...
        <script type="text/javascript" src="/js/client-validation.js"></script>
...
</head>

افزودن اعتبارسنج [assertfalse] صرفاً شامل ایجاد دو تابع زیر jS است:


// -------------- assertfalse
$.validator.addMethod("assertfalse", function(value, element, param) {
    return value === "false";
});

$.validator.unobtrusive.adapters.add("assertfalse", [], function(options) {
    options.rules["assertfalse"] = options.params;
    options.messages["assertfalse"] = options.message.replace("''", "'");
});

راستش را بخواهید، من کارشناس جاوااسکریپت نیستم، زبانی که برای من کاملاً مرموز باقی مانده است. اصول اولیه آن ساده است، اما کتابخانه‌های ساخته‌شده بر روی آن اغلب بسیار پیچیده هستند. برای نوشتن خطوط کد بالا، از کدی که در اینترنت پیدا کردم الهام گرفتم. این لینک [http://jsfiddle.net/LDDrk/] بود که راه را به من نشان داد. اگر هنوز در دسترس باشد، از خوانندگان دعوت می‌شود نگاهی به آن بیندازند، زیرا جامع است و شامل یک مثال عملی می‌باشد. این لینک نشان می‌دهد چگونه یک اعتبارسنج جدید ایجاد کنیم و به من امکان داد تا همه اعتبارسنج‌های این فصل را بسازم. بیایید به کد بازگردیم:

  • خطوط ۲–۴: اعتبارسنج جدید را تعریف می‌کنند. تابع [$.validator.addMethod] به عنوان اولین پارامتر، نام اعتبارسنج را می‌گیرد و به عنوان پارامتر دوم، تابعی که آن را تعریف می‌کند؛
  • خط ۲: این تابع سه پارامتر دارد:
    • [value]: مقداری که باید اعتبارسنجی شود. این تابع باید در صورتی که مقدار معتبر است، [true] را بازگرداند و در غیر این صورت، [false] را بازگرداند؛
    • [element]: عنصر HTML که مقدار مورد اعتبارسنجی به آن تعلق دارد،
    • [param]: ابجکتی حاوی مقادیری است که با پارامترهای اعتبارسنج مرتبط هستند. ما هنوز این مفهوم را معرفی نکرده‌ایم. در اینجا، اعتبارسنج [assertFalse] هیچ پارامتری ندارد. ما می‌توانیم بدون نیاز به اطلاعات اضافی تشخیص دهیم که آیا مقدار [value] معتبر است یا خیر. اما اگر مجبور بودیم بررسی کنیم که آیا مقدار [value] یک عدد حقیقی در بازه [min, max] است، موضوع متفاوت می‌شد. در این صورت، لازم است که مقادیر [min] و [max] را بدانیم. این دو مقدار به عنوان پارامترهای اعتبارسنج شناخته می‌شوند؛
  • خطوط ۶–۹: تابعی مورد نیاز کتابخانه اعتبارسنجی MS. تابع [$.validator.unobtrusive.adapters.add]، به عنوان پارامتر اول خود نام اعتبارسنج، به عنوان پارامتر دوم آرایه پارامترهای اعتبارسنج و به عنوان پارامتر سوم خود تابعی را می‌پذیرد؛
  • اعتبارسنج [assertFalse] هیچ پارامتری ندارد. به همین دلیل پارامتر دوم یک آرایه خالی است؛
  • این تابع تنها یک پارامتر دارد، یک شیء [options] که حاوی اطلاعات مربوط به عنصر مورد تأیید است، که برای آن دو ویژگی جدید، [rules] و [messages]، باید تعریف شوند؛
    • خط ۷: قواعد [rules] برای اعتبارسنج [assertFalse] تعریف شده‌اند. این قواعد پارامترهای اعتبارسنج [assertFalse] هستند، همانند پارامترهای [param] در خط ۲. این پارامترها در [options.params] یافت می‌شوند؛
    • خط ۸: پیام خطا را برای اعتبارسنج [assertFalse] تعریف کنید. این در [options.message] یافت می‌شود. ما با مشکل زیر در پیام‌های خطا مواجه می‌شویم. در فایل‌های پیام، پیام زیر را می‌یابیم:

Range.form01.int1014=La valeur doit être dans l''intervalle [10,14]

آپاستروف دوبل برای Thymeleaf الزامی است. این آن را به عنوان یک آپاستروف تکی تفسیر می‌کند. اگر از آپاستروف تکی استفاده شود، Thymeleaf آن را نمایش نمی‌دهد. اکنون، این پیام‌ها همچنین به عنوان پیام‌های خطا برای کتابخانه اعتبارسنجی MS نیز عمل خواهند کرد. با این حال، جاوا اسکریپت هر دو آپاستروف را نمایش خواهد داد. بنابراین در خط ۸، آپاستروف دوبل را در پیام خطا با یک آپاستروف ساده جایگزین می‌کنیم.

برای درک بهتر آنچه در حال رخ دادن است، می‌توانیم مقداری کد لاگ‌گیری jS اضافه کنیم:


// گزارش‌ها
var logs = {
    assertfalse : true
}


// -------------- assertfalse
$.validator.addMethod("assertfalse", function(value, element, param) {
    // گزارش‌ها
    if (logs.assertfalse) {
        console.log(jSON.stringify({
            "[assertfalse] value" : value
        }));
        console.log("[assertfalse] element");
        console.log(element);
        console.log(jSON.stringify({
            "[assertfalse] param" : param
        }));
    }
    // آزمون اعتبار
    return value === "false";
});

$.validator.unobtrusive.adapters.add("assertfalse", [], function(options) {
    // گزارش‌ها
    if (logs.assertfalse) {
        console.log(jSON.stringify({
            "[assertfalse] options.params" : options.params
        }));
        console.log(jSON.stringify({
            "[assertfalse] options.message" : options.message
        }));
        console.log(jSON.stringify({
            "[assertfalse] options.messages" : options.messages
        }));
    }
    // کد
    options.rules["assertfalse"] = options.params;
    options.messages["assertfalse"] = options.message.replace("''", "'");
});

این کد از کتابخانه jSON JSON3 [http://bestiejs.github.io/json3/] استفاده می‌کند. اگر لاگ‌گیری را فعال کنیم (خط ۳)، خروجی زیر در کنسول نمایش داده می‌شود:

هنگامی که صفحه برای اولین بار بارگذاری می‌شود، لاگ‌های زیر نمایش داده می‌شوند:

 

تابع jS [$.validator.unobtrusive.adapters.add] اجرا شده است. ما موارد زیر را دریافت می‌کنیم:

  • [options.params] یک شیء خالی است زیرا اعتبارسنج [assertFalse] هیچ پارامتری ندارد؛
  • [options.message] پیام خطایی است که برای اعتبارسنج [assertFalse] در ویژگی [data-val-assertFalse] ساخته شده است؛
  • [options.messages] یک شیء است که شامل سایر پیام‌های خطا برای عنصر اعتبارسنجی‌شده می‌باشد. در اینجا پیام خطایی را که در ویژگی [data-val-required] قرار دادیم، می‌یابیم؛

اکنون بیایید یک مقدار نادرست را در فیلد [assertFalse] وارد کرده و اعتبارسنجی کنیم:

 

سپس لاگ‌های زیر را دریافت می‌کنیم:

در اینجا موارد زیر قابل مشاهده است:

  • مقدار در حال آزمایش [true] است (خط 118);
  • عنصر HTML که در حال آزمایش است، دکمه رادیویی با شناسه [assertFalse1] است (خط ۱۲۲);
  • اعتبارسنج [assertFalse] هیچ پارامتری ندارد (خط ۱۲۳);

خب، این هم از این. چه نتیجه‌ای می‌توانیم از همه این‌ها بگیریم؟

برای اعتبارسنج X jS، باید تعریف کنیم:

  • در تگ HTML که باید اعتبارسنجی شود، ویژگی [data-val-X='msg']، که هم اعتبارسنج X و هم پیام خطای آن را تعریف می‌کند؛
  • دو تابع jS که باید در فایل [client-validation.js] گنجانده شوند:
    • [$.validator.addMethod("X", function(value, element, param)],
    • [$.validator.unobtrusive.adapters.add("X", [param1, param2], function(options)];

سپس بر اساس آنچه برای این اعتبارسنج اول انجام شده است، پیش خواهیم رفت و صرفاً موارد جدید را تشریح خواهیم کرد.

6.3.10. اعتبارسنج [asserttrue]

این اعتبارسنج، البته، مشابه اعتبارسنج [assertFalse] است.

خط [1] توسط دنباله زیر از نمای [vue-01.xml] تولید می‌شود:


<!-- الزامی، asserttrue -->
<tr>
    <td class="col1">asserttrue</td>
    <td class="col2">
        <select th:field="*{assertTrue}" data-val="true" th:attr="data-val-asserttrue=#{AssertTrue}">
            <option value="true">True</option>
            <option value="false">False</option>
        </select>
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="assertTrue" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('assertTrue')}" th:errors="*{assertTrue}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [assertTrue] در فرم [Form01] هستند:


    @NotNull
    @AssertTrue
private Boolean assertTrue;

در خطوط ۱–۱۶ چیز جدیدی وجود ندارد. آنها از اعتبارسنج [asserrtrue] استفاده می‌کنند که باید در فایل [client-validation.js] تعریف شود:


// -------------- asserttrue
$.validator.addMethod("asserttrue", function(value, element, param) {
    return value === "true";
});

$.validator.unobtrusive.adapters.add("asserttrue", [], function(options) {
    options.rules["asserttrue"] = options.params;
    options.messages["asserttrue"] = options.message.replace("''", "'");
});

6.3.11. اعتبارسنج‌های [date] و [past]

خط [1] توسط دنباله زیر در نما [vue-01.xml] تولید می‌شود:


<!-- الزامی، تاریخ، گذشته -->
<tr>
    <td class="col1">required, date, past</td>
    <td class="col2">
        <input type="date" th:field="*{dateInPast}" th:value="*{dateInPast}" data-val="true"
            th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-past=#{Past.form01.dateInPast}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="dateInPast" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('dateInPast')}" th:errors="*{dateInPast}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [dateInPast] در فرم [Form01] هستند:


    @NotNull
    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateInPast;

خط حاوی اعتبارسنج‌های تاریخ به شرح زیر است:


<input type="date" th:field="*{dateInPast}" th:value="*{dateInPast}" data-val="true"             th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-past=#{Past.form01.dateInPast}" />

این شامل سه اعتبارسنج است، [data-val-X]: required، date، past. ما باید توابع مرتبط با این دو اعتبارسنج جدید را در [client-validation.js] تعریف کنیم:


logs.date = true;
// -------------- تاریخ
$.validator.addMethod("date", function(value, element, param) {
    // اعتبار
    var valide = Globalize.parseDate(value, "yyyy-MM-dd") != null;
    // گزارش‌ها
    if (logs.date) {
        console.log(jSON.stringify({
            "[date] value" : value,
            "[date] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("date", [], function(options) {
    options.rules["date"] = options.params;
    options.messages["date"] = options.message.replace("''", "'");
});

و


logs.past = true;
// -------------- گذشته
$.validator.addMethod("past", function(value, element, param) {
    // اعتبار
    var valide = value <= new Date().toISOString().substring(0, 10);
    // سوابق
    if (logs.past) {
        console.log(jSON.stringify({
            "[past] value" : value,
            "[past] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("past", [], function(options) {
    options.rules["past"] = options.params;
    options.messages["past"] = options.message.replace("''", "'");
});

قبل از توضیح کد، بیایید نگاهی به لاگ‌ها هنگام وارد کردن تاریخی بعد از امروز بیندازیم:

 

اولین نکته‌ای که باید توجه کرد این است که تاریخ مورد تأیید به صورت یک رشتهٔ کاراکتری در قالب [aaaa-mm-jj] دریافت می‌شود. این موضوع خطوط زیر را توضیح می‌دهد:


var valide = Globalize.parseDate(value, "yyyy-MM-dd") != null;

کتابخانه [globalize.js] تابع [Globalize.parseDate] را که در بالا نشان داده شده است، فراهم می‌کند. پارامتر اول تاریخ به صورت یک رشته و پارامتر دوم فرمت آن است. نتیجه، در صورتی که تاریخ معتبر نباشد، یک اشاره گر به null است؛ در غیر این صورت، تاریخ حاصل خواهد بود.

اعتبار اعتبارسنج [past] توسط کد زیر بررسی می‌شود:


var valide = value <= new Date().toISOString().substring(0, 10);

در اینجا ارزیابی عبارت [new Date().toISOString().substring(0, 10)] در یک کنسول آورده شده است:

  

برای معتبر بودن، رشتهٔ کاراکتری [value] باید قبل از رشتهٔ [new Date().toISOString().substring(0, 10)] به ترتیب حروف الفبا قرار گیرد.

شایان ذکر است که نسخه‌ی کروم مورد استفاده، تاریخ را به صورت [yyyy-mm-dd] نمایش می‌دهد. برای مرورگری که اینطور نیست، کاربر باید صراحتاً موظف شود که از این قالب ورودی استفاده کند.

6.3.12. اعتبارسنج [future]

خط [1] توسط دنباله زیر از نمای [vue-01.xml] تولید می‌شود:


<!-- الزامی، تاریخ، آینده -->
<tr>
    <td class="col1">required, date, future</td>
    <td class="col2">
        <input type="date" th:field="*{dateInFuture}" th:value="*{dateInFuture}" data-val="true" th:attr="data-val-required=#{NotNull},data-val-date=#{DateInvalide.form01},data-val-future=#{Future.form01.dateInFuture}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="dateInFuture" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('dateInFuture')}" th:errors="*{dateInFuture}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [dateInFuture] در فرم [Form01] هستند:


    @NotNull
    @Future
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date dateInFuture;
  • در خط ۵، اعتبارسنج جدید [data-val-future] ظاهر می‌شود؛

این اعتبارسنج، البته، بسیار شبیه اعتبارسنج [past] است. دو تابع که باید به [client-validation.js] اضافه شوند به شرح زیر هستند:


// -------------- آینده
$.validator.addMethod("future", function(value, element, param) {
    var now = new Date().toISOString().substring(0, 10);
    return value > now;
});

$.validator.unobtrusive.adapters.add("future", [], function(options) {
    options.rules["future"] = options.params;
    options.messages["future"] = options.message.replace("''", "'");
});

6.3.13. اعتبارسنج‌های [int] و [max]

خط [1] توسط توالی زیر در view [vue-01.xml] تولید می‌شود:


<!-- الزامی، عدد صحیح، حداکثر (۱۰۰) -->
<tr>
    <td class="col1">required, int, max(100)</td>
    <td class="col2">
        <input type="text" th:field="*{intMax100}" th:value="*{intMax100}" data-val="true"             th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch},data-val-max=#{Max.form01.intMax100},data-val-max-value=#{form01.intMax100.value}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="intMax100" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('intMax100')}" th:errors="*{intMax100}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [intMax100] در فرم [Form01] هستند:


    @NotNull
    @Max(value = 100)
    private Integer intMax100;

در خط ۵، دو اعتبارسنج جدید وجود دارد: [int] و [max]. مورد دوم یک پارامتر دارد: حداکثر مقدار. بیایید کد تولیدشده توسط خط ۵ را که HTML است بررسی کنیم:


<!-- الزامی، عدد صحیح، حداکثر (۱۰۰) -->
<tr>
    <td class="col1">required, int, max(100)</td>
    <td class="col2">
        <input type="text" data-val="true" data-val-int="Format invalide" data-val-max-value="100" data-val-required="Le champ est obligatoire" data-val-max="La valeur doit être inférieure ou égale à 100" value="" id="intMax100" name="intMax100" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="intMax100" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">

    </td>
</tr>

بیایید معنای ویژگی‌های مختلف [data-X] را به یاد بیاوریم:

  • [data-val="true"] نشان می‌دهد که اعتبارسنج‌ها با عنصر HTML مرتبط هستند؛
  • [data-val-required] اعتبارسنج [required] را به همراه پیام آن معرفی می‌کند؛
  • [data-val-int] اعتبارسنج [int] را با پیام آن معرفی می‌کند؛
  • [data-val-max] اعتبارسنج [max] را با پیام آن معرفی می‌کند؛
  • [data-val-max-value="100"] پارامتری به نام [value] را برای اعتبارسنج [max] معرفی می‌کند. [100] مقدار این پارامتر است. این اولین بار است که با مفهوم پارامترهای اعتبارسنج روبه‌رو می‌شویم.

فایل [client-validation.js] با اعتبارسنج زیر به‌روزرسانی شده است: [int]


logs.int = true;
// -------------- عدد صحیح
$.validator.addMethod("int", function(value, element, param) {
    // اعتبار
    valide = /^\s*[-\+]?\s*\d+\s*$/.test(value);
    // گزارش‌ها
    if (logs.int) {
        console.log(jSON.stringify({
            "[int] value" : value,
            "[int] valide" : valide,
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("int", [], function(options) {
    options.rules["int"] = options.params;
    options.messages["int"] = options.message.replace("''", "'");
});
  • خط ۵: یک عبارت منظم برای بررسی اینکه رشته [value] واقعاً نمایانگر یک عدد صحیح است، استفاده می‌شود. این ممکن است دارای علامت باشد؛

در اینجا چند نمونه از لاگ‌ها آورده شده است:

1
2
3
{"[int] value":"x","[int] valide":false}
{"[int] value":"11","[int] valide":true}
{"[int] value":"11x","[int] valide":false}

اعتبارسنج [max] به صورت زیر در [client-validation.js] اضافه می‌شود


// -------------- حداکثر که باید همراه با [int] یا [number] استفاده شود
logs.max = true;
$.validator.addMethod("max", function(value, element, param) {
    // گزارش‌ها
    if (logs.max) {
        console.log(jSON.stringify({
            "[max] value" : value,
            "[max] param" : param
        }));
    }
    // اعتبار
    var val = Globalize.parseFloat(value);
    if (isNaN(val)) {
        // گزارش‌ها
        if (logs.max) {
            console.log(jSON.stringify({
                "[max] valide" : true
            }));
        }
        // نتیجه
        return true;
    }
    var max = Globalize.parseFloat(param.value);
    var valide = val <= max;
    // گزارش‌ها
    if (logs.max) {
        console.log(jSON.stringify({
            "[max] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("max", [ "value" ], function(options) {
    options.rules["max"] = options.params;
    options.messages["max"] = options.message.replace("''", "'");
});

اکنون به مورد پارامتر [value] برای اعتبارسنج [max]، که توسط ویژگی [data-val-max-value="100"] معرفی شده است، می‌پردازیم.

  • در خط ۳۵، پارامتر [value] در پارامتر دوم تابع [$.validator.unobtrusive.adapters.add] گنجانده می‌شود؛
  • خط ۳: شیء [param] دیگر خالی نخواهد بود، بلکه حاوی {"value":100} خواهد بود؛

برای درک کد در خطوط ۳ تا ۳۳، مهم است بدانیم که وقتی چندین اعتبارسنج روی یک عنصر HTML وجود دارد:

  • ترتیب اجرای اعتبارسنج‌ها نامشخص است؛
  • اجرای اعتبارسنج‌ها به محض اینکه یک اعتبارسنج عنصر را نامعتبر اعلام کند، متوقف می‌شود. در این صورت، پیام خطای همان اعتبارسنج به عنصر نامعتبر اختصاص می‌یابد؛

بیایید کد را بررسی کنیم:

  • خط ۱۲: ما بررسی می‌کنیم که یک عدد داریم. اگر اعتبارسنج [int] قبل از اعتبارسنج [max] اجرا شده باشد، این موضوع لزوماً درست است، زیرا یک مقدار نامعتبر، اجرای اعتبارسنج‌ها را متوقف می‌کند؛
  • خطوط ۱۳–۲۲: اگر مقدار عدد نباشد، این بدان معناست که اعتبارسنج [int] هنوز اجرا نشده است. سپس نشان می‌دهیم که مقداری که در حال آزمایش است معتبر است تا اعتبارسنج [int] بتواند وظیفه خود را انجام دهد و عنصر را با پیام خطای مخصوص به خود نامعتبر اعلام کند؛
  • خطوط ۲۳–۲۴: اعتبار [value] را محاسبه می‌کند؛

در اینجا چند لاگ آورده شده است:

ارزش وارد شده
سوابق
x

{"[max] value":"x","[max] param":{"value":"100"}}
{"[max] valide":true}
{"[int] value":"x","[int] valide":false}
111

{"[max] value":"111","[max] param":{"value":"100"}}
{"[max] valide":false}
111x

{"[max] value":"111x","[max] param":{"value":"100"}}
{"[max] valide":true}
{"[int] value":"111x","[int] valide":false}

6.3.14. [min] اعتبارسنج

خط [1] توسط دنباله زیر از نما [vue-01.xml] تولید می‌شود:


<!-- الزامی، عدد صحیح، حداقل(10) -->
<tr>
    <td class="col1">required, int, min(10)</td>
    <td class="col2">
        <input type="text" th:field="*{intMin10}" th:value="*{intMin10}" data-val="true"             th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch},data-val-min=#{Min.form01.intMin10},data-val-min-value=#{form01.intMin10.value}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="intMin10" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('intMin10')}" th:errors="*{intMin10}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [intMin10] در فرم [Form01] هستند:


    @NotNull
    @Min(value = 10)
private Integer intMin10;

خط ۵ یک اعتبارسنج جدید [min] [data-val-int=#{typeMismatch}] را با پارامتر [value] [data-val-min-value=#{form01.intMin10.value}"] معرفی می‌کند. این مشابه اعتبارسنج [max] است. کد زیر را به [client-validation.js] اضافه کنید:


logs.min = true;
//-------------- حداقل باید در کنار [int] یا [number] استفاده شود
$.validator.addMethod("min", function(value, element, param) {
    // گزارش‌ها
    if (logs.min) {
        console.log(jSON.stringify({
            "[min] value" : value,
            "[min] param" : param
        }));
    }
    // اعتبار
    var val = Globalize.parseFloat(value);
    if (isNaN(val)) {
        // گزارش‌ها
        if (logs.min) {
            console.log(jSON.stringify({
                "[min] valide" : true
            }));
        }
        // نتیجه
        return true;
    }
    var min = Globalize.parseFloat(param.value);
    var valide = val >= min;
    // سوابق
    if (logs.min) {
        console.log(jSON.stringify({
            "[min] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("min", [ "value" ], function(options) {
    options.rules["min"] = options.params;
    options.messages["min"] = options.message.replace("''", "'");
});

در اینجا چند گزارش اجرای کد آورده شده است:

مقدار ورودی
لاگ‌ها
x

{"[min] value":"x","[min] param":{"value":"10"}}
{"[min] valide":true}
{"[int] value":"x","[int] valide":false}
11

{"[min] value":"11","[min] param":{"value":"10"}}
{"[min] valide":true}
{"[int] value":"11","[int] valide":true}
8x

{"[min] value":"8x","[min] param":{"value":"10"}}
{"[min] valide":true}
{"[int] value":"8x","[int] valide":false}

6.3.15. [regex] اعتبارسنج

خط [1] توسط دنباله زیر از نمای [vue-01.xml] تولید می‌شود:


<!-- الزامی، regex -->
<tr>
    <td class="col1">required, regex</td>
    <td class="col2">
        <input type="text" th:field="*{strBetween4and6}" th:value="*{strBetween4and6}" data-val="true"    th:attr="data-val-required=#{NotNull},data-val-regex=#{Size.form01.strBetween4and6}, data-val-regex-pattern=#{form01.strBetween4and6.pattern}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="strBetween4and6" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('strBetween4and6')}" th:errors="*{strBetween4and6}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [strBetween4and6] در فرم [Form01] هستند:


    @NotNull
    @Size(min = 4, max = 6)
    private String strBetween4and6;

خط ۵ خروجی زیر را تولید می‌کند: HTML:


<input type="text" data-val="true" data-val-required="Le champ est obligatoire" data-val-regex="La chaîne doit avoir entre 4 et 6 caractères" data-val-regex-pattern="^.{4,6}$" value="" id="strBetween4and6" name="strBetween4and6" />

این تگ اعتبارسنج [regex] [data-val-regex="La chaîne doit avoir entre 4 et 6 caractères"] را با پارامتر [pattern] [data-val-regex-pattern="^.{4,6}$"] معرفی می‌کند. پارامتر [pattern] عبارت منظم (regular expression) است که مقدار مورد تأیید باید با آن بررسی شود. در اینجا، عبارت منظم بررسی می‌کند که رشته بین ۴ تا ۶ کاراکتر از هر نوع را در خود داشته باشد. اعتبارسنج [regex] در کتابخانه اعتبارسنجی MS از پیش تعریف شده است. بنابراین، نیازی به افزودن چیزی به فایل [client-validation.js] نیست.

6.3.16. اعتبارسنج [email]

خط [1] توسط دنباله زیر از نما [vue-01.xml] تولید می‌شود:


<!-- الزامی، ایمیل -->
<tr>
    <td class="col1">required, email</td>
    <td class="col2">
        <input type="text" th:field="*{email}" th:value="*{email}" data-val="true"             th:attr="data-val-required=#{NotNull},data-val-email=#{Email.form01.email}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="email" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [email] در فرم [Form01] هستند:


    @NotNull
    @Email
    @NotBlank
    private String email;

خط ۵ خط زیر را تولید می‌کند HTML:


<input type="text" data-val="true" data-val-required="Le champ est obligatoire" data-val-email="Adresse mail invalide"    value="" id="email" name="email" />

این تگ اعتبارسنج [email] [data-val-email="Adresse mail invalide"] را معرفی می‌کند. اعتبارسنج [email] در کتابخانه اعتبارسنجی MS از پیش تعریف شده است. بنابراین، نیازی به افزودن چیزی به فایل [client-validation.js] نیست.

6.3.17. اعتبارسنج [range]

خط [1] توسط توالی زیر از نمای [vue-01.xml] تولید می‌شود:


<!-- الزامی، عدد صحیح، بازه (10,14) -->
<tr>
    <td class="col1">required, int, range (10,14)</td>
    <td class="col2">
        <input type="text" th:field="*{int1014}" th:value="*{int1014}" data-val="true"            th:attr="data-val-required=#{NotNull},data-val-int=#{typeMismatch}, data-val-range=#{Range.form01.int1014},data-val-range-max=#{form01.int1014.max},data-val-range-min=#{form01.int1014.min}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="int1014" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('int1014')}" th:errors="*{int1014}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [int1014] در فرم [Form01] هستند:


    @Range(min = 10, max = 14)
    @NotNull
    private Integer int1014;

خط ۵ خط زیر را تولید می‌کند HTML:


<input type="text" data-val="true" data-val-range-max="14" data-val-range="La valeur doit être dans l&#39;&#39;intervalle [10,14]" data-val-int="Format invalide" data-val-required="Le champ est obligatoire" data-val-range-min="10" value="" id="int1014" name="int1014" />

این تگ یک اعتبارسنج جدید [range] را معرفی می‌کند [data-val-range="La valeur doit être dans l&#39;&#39;intervalle [10,14]"] که دو پارامتر دارد: [min] [data-val-range-min="10"] و [max] [data-val-range-max="14"].

در فایل [client-validation.js]، اعتبارسنج [range] را به صورت زیر تعریف می‌کنیم:


// -------------- بازه‌ای که باید همراه با [int] یا [number] استفاده شود
logs.range=true
$.validator.addMethod("range", function(value, element, param) {
    // گزارش‌ها
    if (logs.range) {
        console.log(jSON.stringify({
            "[range] value" : value,
            "[range] param" : param
        }));
    }
    // اعتبار
    var val = Globalize.parseFloat(value);
    if (isNaN(val)) {
        // سوابق
        if (logs.min) {
            console.log(jSON.stringify({
                "[range] valide" : true
            }));
        }
        // تکمیل شد
        return true;
    }
    var min = Globalize.parseFloat(param.min);
    var max = Globalize.parseFloat(param.max);    
    var valide = val >= min && val <= max;
    // سوابق
    if (logs.range) {
        console.log(jSON.stringify({
            "[range] valide" : valide
        }));
    }
    // تکمیل شد
    return valide;
});

$.validator.unobtrusive.adapters.add("range", [ "min", "max" ], function(options) {
    options.rules["range"] = options.params;
    options.messages["range"] = options.message.replace("''", "'");
});

این بسیار شبیه اعتبارسنج‌های [min] و [max] است که قبلاً بررسی کرده‌ایم.

در اینجا چند نمونه از لاگ‌ها آورده شده است:

ارزش وارد شده
لاگ‌ها
x

{"[range] value":"x","[range] param":{"min":"10","max":"14"}}
{"[int] value":"x","[int] valide":false}
8

{"[range] value":"8","[range] param":{"min":"10","max":"14"}}
{"[range] valide":false}
11

{"[range] valide":true}
{"[int] value":"11","[int] valide":true}

6.3.18. اعتبارسنج [number]

خط [1] توسط توالی زیر از نما [vue-01.xml] تولید می‌شود:


<!-- double1: الزامی، عدد، بازه (2.3, 3.4) -->
<tr>
    <td class="col1">double1 : required, number, range (2.3,3.4)</td>
    <td class="col2">
        <input type="text" th:field="*{double1}" th:value="*{double1}" data-val="true"
            th:attr="data-val-required=#{NotNull},data-val-number=#{typeMismatch},data-val-range=#{Range.form01.double1},data-val-range-max=#{form01.double1.max},data-val-range-min=#{form01.double1.min}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="double1" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('double1')}" th:errors="*{double1}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [double1] در فرم [Form01] هستند:


    @NotNull
    @DecimalMax(value = "3.4")
    @DecimalMin(value = "2.3")
private Double double1;

خط ۵ خط زیر را تولید می‌کند HTML:


<input type="text" data-val="true" data-val-number="Format invalide" data-val-range-max="3.4"     data-val-range="La valeur doit être dans l&#39;intervalle [2,3-3,4]" data-val-required="Le champ est obligatoire" data-val-range-min="2.3" value="" id="double1" name="double1" />

این تگ یک اعتبارسنج جدید [number] را با ویژگی [data-val-number="Format invalide"] معرفی می‌کند. این اعتبارسنج در فایل [client-validation.js] به شرح زیر تعریف شده است:


// -------------- عدد
logs.number = true;
$.validator.addMethod("number", function(value, element, param) {
    var valide = !isNaN(Globalize.parseFloat(value));
    // لاگ‌ها
    if (logs.number) {
        console.log(jSON.stringify({
            "[number] value" : value,
            "[number] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("number", [], function(options) {
    options.rules["number"] = options.params;
    options.messages["number"] = options.message.replace("''", "'");
});

در اینجا چند نمونه از لاگ‌ها آورده شده است:

ارزش وارد شده
logs
x
 {"[number] value":"x","[number] valide":false}
-2,5
{"[number] value":"-2,5","[number] valide":true}
{"[range] value":"-2,5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":false}
2,5
{"[number] value":"+2,5","[number] valide":true}
{"[range] value":"+2,5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":true}
+2.5
{"[number] value":"+2.5","[number] valide":true}
{"[range] value":"+2.5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":true}

ما می‌دانیم که اعداد حقیقی وابسته به فرهنگ هستند. در بالا، ما در فرهنگ [fr-FR] هستیم. وقتی وارد [2.5] (نمایش آنگلو-ساکسون) می‌شوید، عدد پذیرفته می‌شود. این به دلیل [Globalize.parseFloat] است که هر دو نمایش را می‌پذیرد:

Globalize.parseFloat("3.3")
3.3
Globalize.parseFloat("3,3")
3.3

بیایید به انگلیسی سوئیچ کنیم و [+2,5] و [+2.5] را وارد کنیم. لاگ‌ها به شرح زیر است:

مقدار وارد شده
گزارش‌ها
x
 {"[number] value":"x","[number] valide":false}
2,5
{"[number] value":"+2,5","[number] valide":true}
{"[range] value":"+2,5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":false}
+2.5
{"[number] value":"+2.5","[number] valide":true}
{"[range] value":"+2.5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":true}

یک مشکل در [2,5] وجود دارد. این به عنوان یک عدد حقیقی معتبر اعلام شده است، در حالی که باید [2.5] باشد. این به دلیل [Globalize.parseFloat] است:

Globalize.parseFloat("2,5")
25

در مثال بالا، [Globalize.parseFloat] به ویرگول توجه نمی‌کند و عدد را به عنوان ۲۵ در نظر می‌گیرد. در فرهنگ [en-US]، یک عدد اعشاری ممکن است شامل نقطه اعشاری و ویرگول‌ها باشد، که گاهی برای جدا کردن هزارگان استفاده می‌شوند.

این را می‌توان به صورت زیر بهبود داد:


// -------------- عدد
logs.number = true;
$.validator.addMethod("number", function(value, element, param) {
    //ما فقط محصولات [fr-FR] و [en-US] را مدیریت می‌کنیم
    var pattern_fr_FR = /^\s*[-+]?[0-9]*\,?[0-9]+\s*$/;
    var pattern_en_US = /^\s*[-+]?[0-9]*\.?[0-9]+\s*$/;
    var culture = Globalize.culture().name;
    // بررسی اعتبار
    var valide;
    if (culture === "fr-FR") {
        valide = pattern_fr_FR.test(value);
    } else if (culture === "en-US") {
        valide = pattern_en_US.test(value);
    } else {
        valide = !isNaN(Globalize.parseFloat(value));
    }
    // گزارش‌ها
    if (logs.number) {
        console.log(jSON.stringify({
            "[number] value" : value,
            "[number] culture" : culture,
            "[number] valide" : valide
        }));
    }
    // نتیجه
    return valide;
});
  • خط ۵: عبارت منظم برای یک عدد اعشاری در فرهنگ [fr-FR
  • خط ۶: عبارت منظم برای یک عدد حقیقی در فرهنگ [en-US
  • خط ۷: نام فرهنگ فعلی. در مثال ما، این یکی از دو فرهنگ فهرست‌شده در بالا خواهد بود؛
  • خطوط ۹–۱۶: بررسی اعتبار برای مقدار وارد شده؛
  • خط ۱۵: ما برای حالتی که فرهنگ نه [fr-FR] و نه [en-US] باشد، پیش‌بینی کرده‌ایم؛

لاگ‌ها اکنون موارد زیر را نشان می‌دهند:

فرهنگ [fr-FR]

مقدار وارد شده
لاگ‌ها
x

 {"[number] value":"x","[number] culture":"fr-FR","[number] valide":false}
-2,5

{"[number] value":"-2,5","[number] culture":"fr-FR","[number] valide":true}
{"[range] value":"-2,5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":false}
2,5

{"[number] value":"+2,5","[number] culture":"fr-FR","[number] valide":true}
{"[range] value":"+2,5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":true}
+2.5

{"[number] value":"+2.5","[number] culture":"fr-FR","[number] valide":false}

محصول [en-US]

ارزش وارد شده
logs
x

{"[number] value":"x","[number] culture":"en-US","[number] valide":false}
2,5

{"[number] value":"+2,5","[number] culture":"en-US","[number] valide":false}
+2.5

{"[number] value":"+2.5","[number] culture":"en-US","[number] valide":true}
{"[range] value":"+2.5","[range] param":{"min":"2.3","max":"3.4"}}
{"[range] valide":true}

6.3.19. [custom3] اعتبارسنج

خط [1] توسط دنباله زیر از نما [vue-01.xml] تولید می‌شود:


<!-- double3: الزامی، عدد، custom3 -->
<tr>
    <td class="col1">double3 : required, number, custom3</td>
    <td class="col2">
        <input type="text" th:field="*{double3}" th:value="*{double3}" data-val="true"            th:attr="data-val-required=#{NotNull},data-val-number=#{typeMismatch},data-val-custom3=${custom3.message},data-val-custom3-field=${custom3.otherFieldName},data-val-custom3-max=${custom3.max},data-val-custom3-min=${custom3.min}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="double3" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('double3')}" th:errors="*{double3}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [double3] در فرم [Form01] هستند:


    @NotNull
    private Double double3;

در اینجا، ما می‌خواهیم یک اعتبارسنج را بررسی کنیم که نه یک مقدار وارد شده را، بلکه یک رابطه بین دو مقدار وارد شده را اعتبارسنجی می‌کند. در این مورد، ما می‌خواهیم [double1+double3] در محدوده [10,13] قرار گیرد.

خط ۵ خط زیر را تولید می‌کند: HTML:


<input type="text" data-val="true" data-val-custom3-min="10.0" data-val-number="Invalid format"
    data-val-custom3="[double3+double1] must be in [10,13]" data-val-custom3-max="13.0" data-val-custom3-field="double1"    data-val-required="Field is required" value="" id="double3" name="double3" />

این خط اعتبارسنج جدید [custom3] را معرفی می‌کند که توسط ویژگی [data-val-custom3="[double3+double1] must be in [10,13]"] اعلام شده است. این اعتبارسنج دارای پارامترهای زیر است:

  • [field]، که توسط ویژگی [data-val-custom3-field="double1"] اعلام شده است. این پارامتر فیلدی را مشخص می‌کند که مقدار آن برای محاسبه اعتبار [double3] استفاده می‌شود؛
  • [min] توسط ویژگی [data-val-custom3-min="10.0"] تعریف می‌شود. این پارامتر حداقل مقدار بازه [min, max] است که [double1+double3] باید در آن قرار گیرد؛
  • [max] توسط ویژگی [data-val-custom3-max="13.0"] تعریف می‌شود. این پارامتر حداکثر مقدار بازه [min, max] است که [double1+double3] باید در آن قرار گیرد؛

این اعتبارسنج در [client-validation.js] به شرح زیر پردازش می‌شود:


// -------------- custom3 مورد استفاده در conjunction با [number]
logs.custom3 = true;
$.validator.addMethod("custom3", function(value1, element, param) {
    // مقدار دوم
    var value2 = $("#" + param.field).val();
    // لاگ‌ها
    if (logs.custom3) {
        console.log(jSON.stringify({
            "[custom3] value1" : value1,
            "[custom3] param" : param,
            "[custom3] value2" : value2            
        }))
    }
    // مقدار اول
    var valeur1 = Globalize.parseFloat(value1);
    if (isNaN(valeur1)) {
        // اجازه دهید اعتبارسنج [number] کار را انجام دهد
        if (logs.custom3) {
            console.log(jSON.stringify({
                "[custom3] valide" : true
            }))
        }
        return true;
    }
    // مقدار دوم
    var valeur2 = Globalize.parseFloat(value2);
    if (isNaN(valeur2)) {
        //بررسی اعتبار قابل انجام نیست
        if (logs.custom3) {
            console.log(jSON.stringify({
                "[custom3] valide" : false
            }))
        }
        return false;
    }
    //بررسی اعتبار
    var min = Globalize.parseFloat(param.min);
    var max = Globalize.parseFloat(param.max);
    var somme = valeur1 + valeur2;
    var valide = somme >= min && somme <= max;
    // لاگ‌ها
    if (logs.custom3) {
        console.log(jSON.stringify({
            "[custom3] valide" : valide
        }))
    }
    // نتیجه
    return valide;
});

$.validator.unobtrusive.adapters.add("custom3", [ "field", "max", "min" ], function(options) {
    options.rules["custom3"] = options.params;
    options.messages["custom3"] = options.message.replace("''", "'");
});

در اینجا چند مثال از ورودی‌های لاگ آورده شده است:

مقادیر وارد شده
[double1,double3]
لاگ‌ها
[x,1]

{"[custom3] value1":"1","[custom3] param":{"field":"double1","max":"13.0","min":"10.0"},"[custom3] value2":"x"}
{"[custom3] valide":false}
[1,x]

{"[number] value":"x","[number] culture":"en-US","[number] valide":false}
[1,20]

{"[custom3] value1":"20","[custom3] param":{"field":"double1","max":"13.0","min":"10.0"},"[custom3] value2":"1"}
{"[custom3] valide":false}
[1,10]

{"[number] value":"10","[number] culture":"en-US","[number] valide":true}
{"[custom3] value1":"10","[custom3] param":{"field":"double1","max":"13.0","min":"10.0"},"[custom3] value2":"1"}
{"[custom3] valide":true}

6.3.20. [url] اعتبارسنج

خط [1] توسط دنباله زیر از نما [vue-01.xml] تولید می‌شود:


<!-- الزامی، URL -->
<tr>
    <td class="col1">required, url</td>
    <td class="col2">
        <input type="text" th:field="*{url}" th:value="*{url}" data-val="true"            th:attr="data-val-required=#{NotNull},data-val-url=#{URL.form01.url}" />
    </td>
    <td class="col3">
        <span class="field-validation-valid" data-valmsg-for="url" data-valmsg-replace="true"></span>
    </td>
    <td class="col4">
        <span th:if="${#fields.hasErrors('url')}" th:errors="*{url}" class="error">Donnée erronée</span>
    </td>
</tr>

این خطوط مربوط به فیلد [url] در فرم [Form01] هستند:


    @URL
    @NotBlank
    private String url;

خط ۵ خط زیر را تولید می‌کند HTML:


<input type="text" data-val="true" data-val-url="Invalid URL" data-val-required="Field is required" value="" id="url"    name="url" />

این اعتبارسنج [url] را با ویژگی [data-val-url] معرفی می‌کند. این اعتبارسنج در کتابخانه اعتبارسنجی jQuery از پیش تعریف شده است. نیازی به افزودن چیزی به [client-validation.js] نیست.

6.3.21. فعال‌سازی/غیرفعال‌سازی اعتبارسنجی سمت کلاینت

تا زمانی که اعتبارسنجی سمت کلاینت فعال باشد، اعتبارسنجی سمت سرور هرگز اجرا نخواهد شد، زیرا مقادیر ارسال‌شده تنها در صورتی به سرور می‌رسند که در سمت کلاینت معتبر اعلام شده باشند. برای مشاهده عملکرد اعتبارسنجی سمت سرور، باید اعتبارسنجی سمت کلاینت غیرفعال شود. نمای [vue-01.xml] دو پیوند برای مدیریت این فعال‌سازی/غیرفعال‌سازی فراهم می‌کند:


<a id="clientValidationTrue" href="javascript:setClientValidation(true)">
    <span style="margin-left:30px" th:text="#{client.validation.true}"></span>
</a>
<a id="clientValidationFalse" href="javascript:setClientValidation(false)">
    <span style="margin-left:30px" th:text="#{client.validation.false}"></span>
</a>

این دو لینک همزمان قابل مشاهده نیستند:

مترجم HTML برای این پیوندها به شرح زیر است:


<a id="clientValidationTrue" href="javascript:setClientValidation(true)">
    <span style="margin-left:30px">Activer la validation client</span>
</a>
<a id="clientValidationFalse" href="javascript:setClientValidation(false)">
    <span style="margin-left:30px">Inhiber la validation client</span>
</a>

اسکریپت jS [setClientValidation] در فایل [local.js] تعریف شده است (به بالا مراجعه کنید). در تابع [$(document).ready] در این فایل، لینک‌های اعتبارسنجی استفاده می‌شوند:


//سند آماده
$(document).ready(function() {
    // مراجع کلی
...
    activateValidationTrue = $("#clientValidationTrue");
    activateValidationFalse = $("#clientValidationFalse");
    clientValidation = $("#clientValidation");
...
    // پیوندهای اعتبارسنجی
    //clientValidation یک فیلد پنهان است که توسط سرور تنظیم شده است
    var validate = clientValidation.val();
    setClientValidation2(validate === "true");
});
  • خط ۵: مرجعی به لینک فعال‌سازی اعتبارسنجی سمت کلاینت؛
  • خط ۶: مروری به لینک غیرفعال‌سازی اعتبارسنجی سمت کلاینت؛
  • خط ۷: اشاره‌ای به یک فیلد مخفی در فرم که آخرین وضعیت فعال‌سازی را به صورت مقدار بوله‌ای [true : validation client activée, false : validation client désactivée] ذخیره می‌کند. این فیلد در نما [vue-01.xml] به شکل زیر یافت می‌شود:

<input type="hidden" th:field="*{clientValidation}" th:value="*{clientValidation}" value="true" />

و با فیلد [clientValidation] در فرم [Form01] مطابقت دارد:


// اعتبارسنجی سمت کلاینت
private boolean clientValidation = true;
  • خط ۱۱: مقدار فیلد مخفی بازیابی می‌شود؛
  • خط ۱۲: تابع زیر [setClientValidation2] فراخوانی می‌شود:

function setClientValidation2(activate) {
    // پیوندها
    if (activate) {
        //اعتبارسنجی سمت کلاینت فعال است
        activateValidationTrue.hide();
        activateValidationFalse.show();
        // پارس کردن اعتبارسنج‌های فرم
        $.validator.unobtrusive.parse(formulaire);
    } else {
        //اعتبارسنجی سمت کلاینت غیرفعال است
        activateValidationFalse.hide();
        activateValidationTrue.show();
        //اعتبارسنج‌های فرم در حال غیرفعال شدن هستند
        formulaire.data('validator', null);
    }
}
  • خط ۱: پارامتر [activate] در صورتی که نیاز به فعال‌سازی اعتبارسنجی سمت کلاینت باشد، روی [true] تنظیم می‌شود، در غیر این صورت روی false تنظیم می‌گردد؛
  • خطوط ۵–۶: لینک غیرفعال‌سازی نمایش داده می‌شود، در حالی که لینک فعال‌سازی پنهان است؛
  • خط ۸: برای اینکه اعتبارسنجی سمت کلاینت کار کند، سند باید تجزیه (تحلیل) شود تا به دنبال اعتبارسنج‌ها (validators) با شناسه [data-val-X] بگردد. پارامتر تابع [$.validator.unobtrusive.parse]، شناسه jS فرم مورد تجزیه است؛
  • خطوط ۱۱–۱۲: لینک فعال‌سازی نمایش داده می‌شود، در حالی که لینک غیرفعال‌سازی پنهان است؛
  • خط ۱۴: اعتبارسنج‌های فرم غیرفعال می‌شوند. از این پس، گویی هیچ اعتبارسنج jS در فرم وجود ندارد؛

هدف این تابع [setClientValidation2] چیست؟ از آن برای مدیریت POST استفاده می‌شود. از آنجا که فیلد [clientValidation] یک فیلد مخفی است، همراه با فرم ارسال‌شده توسط سرور ارسال و بازگردانده می‌شود. سپس از مقدار آن برای بازگرداندن اعتبارسنجی سمت کلاینت به وضعیتی که قبل از POST در آن بود، استفاده می‌شود. این به این دلیل است که هیچ وضعیتی از jS بین درخواست‌ها حفظ نمی‌شود. بنابراین، سرور باید اطلاعات لازم برای راه‌اندازی jS را در نمای جدید ارسال کند. این کار معمولاً در تابع [$(document).ready] انجام می‌شود.

بیایید به تابع [setClientValidation] بازگردیم که کلیک‌ها روی پیوندهای فعال‌سازی یا غیرفعال‌سازی اعتبارسنجی سمت کلاینت را مدیریت می‌کند:


//اعتبارسنجی سمت کلاینت
function setClientValidation(activate) {
    // مدیریت فعال‌سازی/غیرفعال‌سازی اعتبارسنجی سمت کلاینت
    setClientValidation2(activate);
    // انتخاب کاربر در فیلد مخفی ذخیره می‌شود
    clientValidation.val(activate ? "true" : "false");
    //تنظیمات بیشتر
    if (activate) {
        //اعتبارسنجی سمت کلاینت فعال است
        // تمام پیام‌های خطا را از سرور پاک می‌کند
        clearServerErrors();
        // فرم ارسال شد
        formulaire.validate().form();
    } else {
        //اعتبارسنجی سمت کلاینت غیرفعال است
        // تمام پیام‌های خطای کلاینت پاک می‌شوند
        clearClientErrors();
    }
}
  • خط ۴: ما از تابع [setClientValidation2] که همین حالا بررسی کردیم استفاده می‌کنیم؛
  • خط ۶: انتخاب کاربر در فیلد مخفی ذخیره می‌شود تا هنگام بازگشت بعدی POST قابل بازیابی باشد؛
  • خط ۱۱: اگر اعتبارسنجی سمت کلاینت فعال باشد، پیام‌های خطا را از ستون [serveur] در نما پاک می‌کنیم. ما تابع [clearServerErrors] را در بخش ۶.۳.۷ شرح دادیم؛
  • خط ۱۳: اعتبارسنج‌های jS برای نمایش هرگونه پیام خطا در ستون [client] نما اجرا می‌شوند؛
  • خط ۱۷: اگر اعتبارسنجی سمت کلاینت غیرفعال باشد، پیام‌های خطا در ستون [client] نما پاک می‌شوند. بیایید کد HTML را برای عنصومی که خطا دارد در کنسول توسعه‌دهنده کروم بررسی کنیم:

<td class="col2">
    <input type="text" data-val="true" data-val-int="Format invalide" data-val-max-value="100" data-val-required="Le champ est obligatoire" data-val-max="La valeur doit être inférieure ou égale à 100" value="" id="intMax100" name="intMax100" aria-required="true"        class="input-validation-error" aria-describedby="intMax100-error">
</td>
<td class="col3">
    <span class="field-validation-error" data-valmsg-for="intMax100" data-valmsg-replace="true">
        <span id="intMax100-error" class="">Le champ est obligatoire</span>
    </span>
</td>
  • در خط ۲، می‌توانیم ببینیم که در ستون ۲ جدول، عنصر دارای خطا سبک [class="input-validation-error"] را دارد؛
  • در خط ۵، می‌بینیم که در ستون ۳ جدول، پیام خطا دارای استایل [class="field-validation-error"] است؛

این موضوع برای همه عناصر خطا صدق می‌کند. ما از این دو قطعه اطلاعات در تابع زیر [clearClientErrors] استفاده می‌کنیم:


//پاک کردن خطاهای کلاینت
function clearClientErrors() {
    //پاک کردن پیام‌های خطای کلاینت
    $(".field-validation-error").each(function(index) {
        $(this).text("");
    });
    //تغییر کلاس CSS برای ورودی‌های نادرست
    $(".input-validation-error").each(function(index) {
        $(this).removeClass("input-validation-error");
    });
}
  • خطوط ۴–۶: ما تمام عناصر با کلاس DOM را که دارای کلاس [field-validation-error] هستند جستجو کرده و متن نمایش‌داده‌شده توسط آن‌ها را پاک می‌کنیم. به این ترتیب پیام‌های خطا پاک می‌شوند؛
  • خطوط ۸–۱۰: تمام عناصر با شناسه DOM که دارای کلاس [input-validation-error] هستند جستجو شده و این کلاس از آن‌ها حذف می‌شود. در نتیجه، عنصر خطایی که به رنگ قرمز برجسته شده بود به سبک اصلی خود بازمی‌گردد؛