Проблема:
Есть страничка, на которую подгружается форма в указанный div.
В контроллере стандартный (почти) код, только контент рендерится частично:
Код: Выделить всё
public function actionCreate()
{
$model = new Client();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->renderPartial('ok');
}
return $this->renderPartial('_form', [
'model' => $model,
]);
}
Если изменить на renderAjax - появляется другая проблема: если виджет имеет свой ассет, и еще с зависимостями - то в подгружаемый контент попадают все эти скрипты, которые, в том числе, дублируют уже загруженные на основной странице. И это часто вызывает проблемы.
Основной вопрос - как подгружать контент, в котором бы подгружались только те ассеты, которые еще не загружены, и выполнялись скрипты.
Варианты, которые рассматривал:
1. отказаться от стандартных динамических виджетов, и инициализировать нужные поля после загрузки ajax (вариант - в шаблоне формы прописать инициализацию вручную, без использования registerJs). Минусы - очень неудобно на больших формах с разнообразием полей, и если виджеты внутри себя динамически генерят параметры или код.
2. подгружать в iframe - частично решает проблему, но есть свои особенности и неудобства.
Мое временное решение следующее:
Покопался в коде фреймворка и нашел метод View->renderBodyEndHtml, который, делает почти то, что мне нужно. Но есть 2 НО:
1. он protected
2. он не вызывает скрипты, добавленные через registerJs, которые с POS_HEAD (для этого есть renderBodyBeginHtml). Некоторые виджеты так инициализируют используемую библиотеку или какие-то параметры.
В итоге, на основе метода renderBodyEndHtml сделал хэлпер:
Код: Выделить всё
public static function renderJs(View $view): string
{
$lines = [];
// Тут, по идее, должны были бы подгружаться данные из ассетов (но это не точно), но их нет, что меня устраивает.
if (!empty($view->jsFiles[View::POS_END])) {
$lines[] = implode("\n", $view->jsFiles[View::POS_END]);
}
if (!empty($view->js[View::POS_HEAD])) {
$lines[] = Html::script(implode("\n", $view->js[View::POS_HEAD]));
}
if (!empty($view->js[View::POS_END])) {
$lines[] = Html::script(implode("\n", $view->js[View::POS_END]));
}
if (!empty($view->js[View::POS_READY])) {
$js = "jQuery(function ($) {\n" . implode("\n", $view->js[View::POS_READY]) . "\n});";
$lines[] = Html::script($js);
}
if (!empty($view->js[View::POS_LOAD])) {
$js = "jQuery(window).on('load', function () {\n" . implode("\n", $view->js[View::POS_LOAD]) . "\n});";
$lines[] = Html::script($js);
}
return empty($lines) ? '' : implode("\n", $lines);
}
Для моих целей, в принципе, проблема решена (тоесть пока все работает).
Но я считаю, что это, не оптимальное и не очень красивое решение.