# Form Validate

### Прізвище

Може складатися з двох частин (*Карпенко-Карий*), які пишуться через **дефіс**

```
maxlength="30"
placeholder="Іваненко"
pattern="[А-яІіЇїЄєҐґ\-'’]{2,30}"
required
```

### Ім’я

Може складатися з двох слів (*Анна Марія*), тому тут є **пробіл**

```
maxlength="30"
placeholder="Василь"
pattern="[А-яІіЇїЄєҐґ\s'’]{2,30}"
required
```

### По батькові

```
maxlength="30"
placeholder="Федорович"
pattern="[А-яІіЇїЄєҐґ'’]{2,30}"
```

### email

```
maxlength="50"
placeholder="name@example.com"
pattern="[0-9A-Z_a-z\.\-]+@[0-9A-Z_a-z\.\-]+?\.[a-z]{2,}"
required
```

### Перевірити поля по шаблонам і піти на іншу сторінку або скрипт:

В form є action і onsubmit.

onsubmit - запускає bootstrap-перевірку полів, і якщо вона проходить - виконується action.

Поля з pattert - перевіряються по цьому шаблону, якщо у поля є тільки required - поле перевіряєтся на не порожність. Якщо нема ні того, ні іншого - поле не перевіряється.

{% code title="HTML" %}

```html
<form id="mainform" action="user_login.py" method="POST" class="row g-3 needs-validation" novalidate onsubmit="is_valid();">
    <div class="col-md-4">
        <label for="login" class="form-label">{{ TX_LOGIN }}</label>
        <input type="text" class="form-control" id="login" name="login" aria-describedby="loginHelpBlock" pattern="^[\.@\d\-\A-Za-z]+$" required>
        <div id="loginHelpBlock" class="form-text">{{ TX_HELP_LOGIN }}</div>
        <div class="invalid-feedback">{{ TX_INVALID_INPUT }}</div>
    </div>
    <div class="col-md-8">
    </div>

    <div class="col-md-4">
        <label for="password" class="form-label">{{ TX_PASSWORD }}</label>
        <input type="password" class="form-control" id="password" name="password" aria-describedby="passwordHelpBlock" required>
        <div id="passwordHelpBlock" class="form-text">{{ TX_HELP_PASSWORD }}</div>
        <div class="invalid-feedback">{{ TX_INVALID_INPUT }}</div>
    </div>
    <div class="col-md-8">
    </div>

    <div class="col-12 py-2" id="alertPlace"></div>
    <div class="col-12 text-center">
        <button type="submit" id="bSave" class="btn btn-primary w-50" >{{ TX_ENTER }}</button>
    </div>
</form>

```

{% endcode %}

{% code title="JS" %}

```javascript
function is_valid()
{
    if (!mainform.checkValidity())
    {
        // "custom Bootstrap form validation" (перевірка за шаблонами)
        event.preventDefault();
        event.stopPropagation();
        // для того, щоб показати які поля не пройшли провірку:
        mainform.classList.add('was-validated');
    }
    else
    {
        // якщо пройшло, щоб не залишалися "зелені" поля з галочками
        mainform.classList.remove('was-validated');
    }
}
```

{% endcode %}

### Перевірити поля по шаблонам (потім виконати AJAX-запит) і залишитися  на цій сторінці:

В цьому прикладі, крім bootstrap-перевірки полів за шаблонами mainform.checkValidity(), ще відбувається перевірка за значеннями: check\_param(), потім вже виконується AJAX-запит save\_param().

```html
<form id="mainform" class="row g-3 needs-validation" novalidate onsubmit="return false;">

    <div class="col-md-4">
        <label for="tTemp" class="form-label">{{ TX_TEMPERATURE }}, °С</label>
        <input type="text" class="form-control" id="tTemp" name="t_temp" value="{{ TEMPERATURE }}" aria-describedby="tempHelpBlock" autocomplete="off" pattern="^-*\d{1,2}(?:[\.,]\d{0,2})?$">
        <div id="tempHelpBlock" class="form-text">{{ TX_AVERAGE_VALUE }}: <b id="avTemp">{% if AV_TEMP %}{{ AV_TEMP }}{% endif %}</b> °С</div>
        <div class="invalid-feedback">{{ TX_INCORRECT_VALUE }} (-25°С ... 45°С)</div>
    </div>
    <div class="col-md-4">
        <label for="nHumd" class="form-label text-end">{{ TX_HUMIDITY }}, %</label>
        <input type="number" class="form-control" id="nHumd" name="n_humd" value="{{ HUMIDITY }}" aria-describedby="humdHelpBlock" min="10" max="90" step="1">
        <div id="humdHelpBlock" class="form-text">{{ TX_AVERAGE_VALUE }}: <b id="avHumd">{% if AV_HUMD %}{{ AV_HUMD }}{% endif %}</b> %</div>
        <div class="invalid-feedback">{{ TX_INCORRECT_VALUE }} (10% ... 90%)</div>
    </div>
    <div class="col-md-4">
        <label for="tPres" class="form-label text-end">{{ TX_PRESSURE }}, гПа</label>
        <input type="text" class="form-control" id="tPres" value="{{ PRESSURE }}" name="t_pres" aria-describedby="presHelpBlock" autocomplete="off" pattern="^\d{3,4}(?:[\.,]\d{0,2})?$">
        <div id="presHelpBlock" class="form-text">{{ TX_AVERAGE_VALUE }}: <b id="avPres">{% if AV_PRES %}{{ AV_PRES }}{% endif %}</b> гПа</div>
        <div class="invalid-feedback">{{ TX_INCORRECT_VALUE }} (950 - 1050 гПа)</div>
    </div>

    <button type="submit" id="bSave" class="btn btn-primary w-50">{{ TX_SAVE }}</button>
</form>

```

```javascript
// --------------------------------------------------------------
mainform.addEventListener('submit', event =>
{
    if (!mainform.checkValidity())
    {
        // спочатку "custom Bootstrap form validation" (перевірка за шаблонами)
        event.preventDefault();
        event.stopPropagation();
        // для того, щоб показати які поля не пройшли провірку
        mainform.classList.add('was-validated');
    }
    else
    {
        // потім - перевірка за значеннями, вимикаємо дію псевдо-класів :invalid та :valid
        mainform.classList.remove('was-validated');
        // і будемо використовувати класи .is-valid та .is-invalid для
        // позначення полів, що не пройшли перевірку
        // https://getbootstrap.com/docs/5.2/forms/validation/
        if (check_param())
        {
            bSave.disabled = true;
            save_param();
        }
        else
        {
            return false;
        }
    }
}, false)


/* --------------------------------------------------------------------- */
function check_param()
{
    if ((tTemp.value + nHumd.value + tPres.value).trim() == '')
    {
        if (!confirm(TX_EMPTY_CONFIRM))
        {
            get_param();
            bSave.disabled = false;
            return false;
        }
    }
    else
    {
        let err = 0;

        if (tTemp.value.trim() != '' && (tTemp.value * 1 < -25 || tTemp.value * 1 > 45))
        {
            valid(tTemp, false);
            err = 1;
        } else {
            valid(tTemp, true);
        }

        if (nHumd.value.trim() != '' && (nHumd.value * 1 < 10 || nHumd.value * 1 > 90))
        {
            valid(nHumd, false);
            err = 1;
        } else {
            valid(nHumd, true);
        }

        if (tPres.value.trim() != '' && (tPres.value * 1 < 950 || tPres.value * 1 > 1050))
        {
            valid(tPres, false);
            err = 1;
        } else {
            valid(tPres, true);
        }

        if (err == 1) return false;
    }
    return true;
}

/* --------------------------------------------------------------------- */
function valid(elem, bl)
{
    if (bl == true)  // set valid
    {
        elem.classList.remove('is-invalid');
        elem.classList.add('is-valid');
    }
    else if (bl == false)  // set invalid
    {
        elem.classList.remove('is-valid');
        elem.classList.add('is-invalid');
    }
    else  // clear all
    {
        elem.classList.remove('is-valid');
        elem.classList.remove('is-invalid');
    }
}

/* --------------------------------------------------------------------- */
function input_disabled(bool)
{
    tTemp.disabled = bool;
    nHumd.disabled = bool;
    tPres.disabled = bool;
    bSave.disabled = bool;
}

/* --------------------------------------------------------------------- */
function save_param_success(resp) {

    const json_obj = resp;
    if (resp && 'res' in resp)
    {
        if (resp.res === 'err')  // error
        {
            errmess = ERROR;
            num_errors = resp.err.length;
            for (let i = 1; i <= num_errors; i++)
            {
                errmess += '<br>' + i +'. '+ resp.err[i-1]
            }
            valid(tTemp);
            valid(nHumd);
            valid(tPres);
            alert('danger', errmess);
        }
        else if (resp.res === 'sux')
        {
            alert('hide');
            // message('sux', resp.sux); // not winXP!
            alert('success', resp.sux);
        }
    }
    else
        alert('danger', SRV_UNKNOWN_RESPONSE);
}

/* --------------------------------------------------------------------- */
function save_param_error(err) {
    console.dir(err);
    alert('danger', SRV_ERR);
}

/* --------------------------------------------------------------------- */
function save_param() {

    let fdata = new FormData(document.forms.mainform);

    fetch("/atmo/save/", { method: "POST", body: fdata })

    .then(resp => {
        if (resp.status != 200) {
            alert('danger', SRV_REFUSED_REQUEST +` Status: ${resp.status}`);
        }
        return resp.json();  // return server response as json
    })
    .then(resp => save_param_success(resp))
    .catch(err => save_param_error(err));
    // return false;  // prevent form submit
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://olexsyn.gitbook.io/enote/progr/bootstrap/form-validate.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
