Добавление динамических полей с помощью yiiActiveForm js

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
nickdenry
Сообщения: 99
Зарегистрирован: 2015.10.28, 04:55

Добавление динамических полей с помощью yiiActiveForm js

Сообщение nickdenry »

Доброго времени суток!

К сожалению не могу найти вменяемую доку по созданию динамических полей для форм в yii2

Пытаюсь добавить поле через js, ка написано в cookbook https://yii2-cookbook.readthedocs.org/f ... veform-js/

Код: Выделить всё

$('#customer-form').yiiActiveForm('add', {
        'id': 'customerphone-number',
        'name': 'CustomerPhone[number]',
        'container': '.field-address',
        'input': '#customerphone-number2',
        'error': '.field-address .help-block'
    });
Получаю ошибку

Код: Выделить всё

TypeError: $form.data(...) is undefined    
$form.data('yiiActiveForm').attributes.push(attribute); 
в yii.activeForm.js (строка 227, столбец 13)

Код формы -

Код: Выделить всё

<div class="customer-form">

    <?php $form = ActiveForm::begin([
        'id' => 'customer-form',
    ]); ?>

    <?= $form->field($modelCustomer, 'name')->textInput(['maxlength' => true])->label('ФИО') ?>

    <?= $form->field($modelCustomerPhone, 'number')->textInput() ?>>

    <div class="form-group">
        <?= Html::submitButton($modelCustomer->isNewRecord ? 'Создать' : 'Обновить', ['class' => $modelCustomer->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>
Подскажите, пожалуйста, все ли правильно у меня с параметрами? Задача вроде бы многократно поднималась на форуме, но решения с yiiActiveForm js не видел;

В общем виде задача выглядит так:

1. форма с полями

[имя] - из модели Customer
[phone] - из модели CusomerPhone, которое может добавляться или убираться динамически, валидироваться

2. Отправка формы, сохранение полей в модели
Ответственные программисты с высоким уровнем технического долга (c)
nickdenry
Сообщения: 99
Зарегистрирован: 2015.10.28, 04:55

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение nickdenry »

Удалось раскопать кое-что.

1. yiiActiveForm инициализируется после общего списка скриптов inline-кодом, соотвественно если я подключаю файл <script src="/js/testForm.js"></script>через AppAsset, то событие инициализации yiiActiveForm происходит позже, чем вызывается код в указанном файле.

Временно решил проблему через вызов кода по событию $(window).on('load') вместо jQuery(document).ready. В связи с этим вопрос - есть ли способ подключить скрипт "ниже" чем inline-код в yii? Либо, возможно, у yiiActiveForm есть какое-то событие инициализации?

скрипт подключается ТУТ (последняя строчка)

Код: Выделить всё

<script src="/assets/fabadf61/jquery.js"></script>
<script src="/assets/3d6b6680/yii.js"></script>
<script src="/assets/3d6b6680/yii.validation.js"></script>
<script src="/assets/3d6b6680/yii.captcha.js"></script>
<script src="/assets/3d6b6680/yii.activeForm.js"></script>
<script src="/js/testForm.js"></script> 
Код инициализации идет ниже:

Код: Выделить всё

<script src="/assets/df91ef16/js/bootstrap.js"></script>
<script type="text/javascript">jQuery(document).ready(function () {
jQuery('#contactform-verifycode-image').yiiCaptcha({"refreshUrl":"\/index.php?r=site%2Fcaptcha\u0026refresh=1","hashKey":"yiiCaptcha\/site\/captcha"});
jQuery('#contact-form').yiiActiveForm([{"id":"contactform-name","name":"name","container":".field-contactform-name","input":"#contactform-name","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Name cannot be blank."});}},{"id":"contactform-email","name":"email","container":".field-contactform-email","input":"#contactform-email","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Email cannot be blank."});yii.validation.email(value, messages, {"pattern":/^[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/,"fullPattern":/^[^@]*<[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/,"allowName":false,"message":"Email is not a valid email address.","enableIDN":false,"skipOnEmpty":1});}},{"id":"contactform-subject","name":"subject","container":".field-contactform-subject","input":"#contactform-subject","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Subject cannot be blank."});}},{"id":"contactform-body","name":"body","container":".field-contactform-body","input":"#contactform-body","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Body cannot be blank."});}},{"id":"contactform-verifycode","name":"verifyCode","container":".field-contactform-verifycode","input":"#contactform-verifycode","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.captcha(value, messages, {"hash":642,"hashKey":"yiiCaptcha/site/captcha","caseSensitive":false,"message":"The verification code is incorrect."});}}], []);
jQuery('#w2-success-0').alert();
});</script>
Есть ветка форума на аналогичную тему, но прямого ответа нет http://www.yiiframework.ru/forum/viewtopic.php?t=17037

2. В подключаемом файле, произвожу добавление произвольного поля к стандартной форме $('#contact-form') и подключаю валидацию - все работает. Но насколько корректно делать это именно таким способом, есть ли автоматизация в шаблоне формы по созданию динамических полей или их прототипов (по типу template)?

Код: Выделить всё

$(window).on('load', function() {

    $('<div class="form-group field-contactform-address required"> \
<label for="contactform-name" class="control-label">Address</label> \
<input type="text" name="ContactForm[address]" class="form-control" id="address"> \
<p class="help-block help-block-error"></p> \
</div>').appendTo($('#contact-form'));

    $('#contact-form').yiiActiveForm('add', {
        'id': 'address',
        'name': 'address',
        'container': '.field-contactform-address',
        'input': '#address',
        'error': '.help-block.help-block-error',
        'validate': function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Address cannot be blank."});}
    });
}); 
Ответственные программисты с высоким уровнем технического долга (c)
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение samdark »

Обычно поля добавляют по кнопочке «+». К моменту её нажатия скрипты ActiveForm, как правило, уже загружены.
nickdenry
Сообщения: 99
Зарегистрирован: 2015.10.28, 04:55

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение nickdenry »

@Sam_Dark да, это правильный use case :) Нужно попробовать.

У меня еще вопрос - есть ли в yii2 возможность хранить template поля "из-коробки", например в data-атрибутах? Нечто аналогичное есть в symfony2 в виде атрибута, например

Код: Выделить всё

data-prototype="<div class="fields-list-item"><div class="input-group"><input type="text" id="delivery_crmbundle_customer_phone___name___number" name="delivery_crmbundle_customer[phone][__name__][number]" required="required" maxlength="10" class="form-control" /><span class="input-group-btn"><button class="btn btn-default btn-sm collection-remove" type="button" title="remove_phone"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button></span></div></div>"
Ответственные программисты с высоким уровнем технического долга (c)
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение samdark »

Из коробки нет, но на jQuery реализовать вроде не сложно:

Код: Выделить всё

var form = $('#my-form');
form.append(form.find('.template').data('prototype'));
nickdenry
Сообщения: 99
Зарегистрирован: 2015.10.28, 04:55

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение nickdenry »

@Sam_Dark

Нет, нет, как с jQuery сделать понятно, я имел ввиду есть ли что-то типа генерации (serialize) прототипа поля из виджета yii?

На примере стандартной формы

Вместо, например

Код: Выделить всё

<?= $form->field($model, 'name') ?>

Код: Выделить всё

<?= $form->field($model, 'name', ['attributes'=>['data-template' => $form->field($model, 'name')->serialize()]]); ?>
или что-то подобное чтобы вывести поле в виде data-атрибута?

Можно без конкретных примеров, меня интересует в какую сторону копать в общем смысле.
Ответственные программисты с высоким уровнем технического долга (c)
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение samdark »

Нет.
nickdenry
Сообщения: 99
Зарегистрирован: 2015.10.28, 04:55

Re: Добавление динамических полей с помощью yiiActiveForm js

Сообщение nickdenry »

@Sam_Dark спасибо;

Не знаю, насколько корректно по меркам yii2, но вроде бы приемлемо.
Мое решение по прототипу для поля формы. Соотвествественно - в view _form.php

Код: Выделить всё

<?php $form = ActiveForm::begin([
            'options' => [
                'class' => 'dynamic-form',
            ],
]); ?>

<!-- Здесь код остальной формы -->

<?php
        /**
         * Погдотовка прототипа поля CustomerPhone.
         */

        // Шаблон поля 
        $phoneFieldTemplate = "<div class=\"input-group col-md-2\">{input}<span class=\"input-group-btn\"><button type=\"button\" class=\"btn btn-default btn-add\">+</button></span></div>\n{hint}\n{error}";
        // Получаем класс модели для первой модели из массива.
        $modelCustomerPhoneClass = get_class(reset($modelCustomerPhones));
        // Создаем пустую модель этого же класса для подготовки прототипа;
        $templateModelCustomerPhone = new $modelCustomerPhoneClass;
        // Создаем поле для модели;
        // Вместо числового индекса используем placeholder __index__;
        $modelField = $form->field($templateModelCustomerPhone, '[__index__]number', ['template'=>$phoneFieldTemplate])->textInput();
        // Создаем прототип
        $modelFieldPrototype = htmlspecialchars($modelField);
    ?>
    
    <?php
       /**
        * Непосредственно вывод поля с использованием прототипа и шаблона.
        */
      ?>       

    <div class="form-group" data-prototype="<?php echo $modelFieldPrototype; ?>">
        <label class="control-label"><?= \Yii::t('app', 'Phone number'); ?></label>
        <div class="fileds-list">
            <?php
                foreach ($modelCustomerPhones as $key => $modelCustomerPhone)
                {
                    echo($form->field($modelCustomerPhone, '['.$key.']number',
                        ['template'=>$phoneFieldTemplate])->textInput()
                    );
                }
            ?>
        </div>
    </div>

   <!-- Здесь код остальной формы -->

    <?php ActiveForm::end(); ?>
Соотвественно, обработка в JS. Временно - без клиентской валидации.

Код: Выделить всё

$(document).ready(function() {
    if ($('form.dynamic-form').length > 0) {
        // Устанавливаем индекс полей подсчетом количества в списке.
        $('.fileds-list').each(function(){
            $(this).data('index', $(this).children().length);
        });

        // Обработка кнопки "добавить" 
        $('.fileds-list').on('click', '.btn-add', function() {
           var fieldsList = $(this).closest('.fileds-list');
           var fieldPrototypeTemplate = $(this).closest('[data-prototype]').data('prototype');
           var fieldListIndex = fieldsList.data('index');
           var fieldPrototype = fieldPrototypeTemplate.replace(/__index__/g, fieldListIndex);
           fieldsList.data('index', fieldListIndex+1); //При добавлении поля увеличиваем индекс на 1
           fieldsList.append(fieldPrototype);
           $(this)
                .toggleClass('btn-default btn-add btn-danger btn-remove')
                .html('–');
        });

        // Обработка кнопки "удалить"
        $('.fileds-list').on('click', '.btn-remove', function() {        
            $(this).closest('.form-group').remove();
        });
    }
});

Ответственные программисты с высоким уровнем технического долга (c)
Ответить