Obalení šablony snippetAreou vložené přes preg_replace

Alsatian
Člen | 164
+
0
-

Ahoj.

Tímto kódem měním text z databáze na vykreslení formuláře

if(preg_match('/{kontaktni-formular}/', $pageHtmlText)) {
	$includeTemplate = $this->createTemplate();
	$includeTemplate->setFile(__DIR__ . '/templates/Page/include.contact-form.latte');

	$pageHtmlText = preg_replace('/{{?kontaktni-formular}?}/', (string) $includeTemplate, $pageHtmlText, 1);
	}

Takto vložený formulář (komponenta) funguje a vykreslím jej šabloně pomocí control (nebo ručně v mém případě).

Nevím, jak mám takto vložený formulář nyní obalit do snippetArea. Je ajaxový a v šabloně chci vypsat chyby, případně info o odeslání. Mimo šablonu vykreslení chyb funguje, předpokládám, že chybí jen ono obalení do snippetArea.

Předem moc děkuji za popostrčení :)

Alsatian
Člen | 164
+
0
-

Blok s textem vypsaným z databáze mám v layout.latte obalen takto:

{snippetArea content}
	{include content}
{/snippetArea}

V šabloně mám potom další snippet

{snippet footerForm}

<form n:name="footerForm">
	{snippet footerFormError}
		{ifset $form}
			<div n:if="$form->hasErrors()" class="alert alert-danger" role="alert">
				<li n:foreach="$form->ownErrors as $error">{$error}</li>
			</div>
		{/ifset}
	{/snippet}

	{snippet footerFormBody}
	...

A invaliduji následovně:

public function formSucceeded($form, $values)
{
	$p = $form->getPresenter();

	$p->redrawControl('content');
	$p->redrawControl('footerForm');
	$p->redrawControl('footerFormError');
	...

Editoval Alsatian (5. 2. 2019 11:18)

David Matějka
Moderator | 6445
+
0
-

zkusil bych to misto jako sablonu renderovat jako control. Udelej si tedy samostatny UI\Control a v tom replace budes mit neco jako

if(preg_match('/{kontaktni-formular}/', $pageHtmlText)) {
	ob_start();
	$this['contactForm']->render(); // musis mit createComponentContactForm
	$formTemplate = ob_get_clean();

    $pageHtmlText = preg_replace('/{{?kontaktni-formular}?}/', (string) $formTemplate, $pageHtmlText, 1);
}
Alsatian
Člen | 164
+
0
-

@DavidMatějka zkusil jsem, při odeslání obdržím chybu „Argument 1 passed to App\Presenters\PagePresenter::App\Presenters\{closure}() must be an instance of App\Presenters\Form, instance of Nette\Application\UI\Form given“

Takto se ale vykreslí formulář automaticky, já jej potřebuji vykreslit ručně. Je to kontaktní formulář na webu, ty se většinou stylují individuálně. Proto jsem jej měl v šablonce.

Tajně doufám, že tam mám jenom nějakou chybku, kterou nevidím :) A že se mi jen formulář nedaří invalidovat. Jinak totiž funguje na výbornou :)

Editoval Alsatian (5. 2. 2019 11:30)

David Matějka
Moderator | 6445
+
0
-

zkusil jsem, při odeslání obdržím chybu „Argument 1 passed to App\Presenters\PagePresenter::App\Presenters\{closure}() must be an instance of App\Presenters\Form, instance of Nette\Application\UI\Form given“

nemáš v Nette\Application\UI\Form v use statementech (v importech)

Je to kontaktní formulář na webu, ty se většinou stylují individuálně. Proto jsem jej měl v šablonce.

té komponentě můžes nastavit, kterou šablonu má vykreslit

Tajně doufám, že tam mám jenom nějakou chybku, kterou nevidím :) A že se mi jen formulář nedaří invalidovat. Jinak totiž funguje na výbornou :)

spíše ne, obávám se, že to budeš muset udělat přes tu komponentu, jak píšu

Alsatian
Člen | 164
+
0
-

@DavidMatějka prosím tě ještě :) Nedaří se mi nastavit šablonku mé komponentě. Googlil jsem, ale chytrý z toho nejsem.
Zkusil jsem přidat do FooterFormFactory.php

public function create()
{
	$form = $this->factory->create();
	$form->getElementPrototype()->class("mbr-form ajax");

	$this->template->render(__DIR__ . '/../presenters/templates/Page/include.contact-form.latte');
	...

což skončí chybou „Component '' is not attached to ‚Nette\Application\UI\Presenter‘“

David Matějka
Moderator | 6445
+
0
-

ten formular musis obalit komponentou, jako je to tady treba: https://doc.nette.org/…s/form-reuse#…

ta komponenta musi mit render metodu, ta v ukazce chybi. a v komponente budes mit (krome tech zminenych veci) i neco jako

private $templateFile;

public function setTemplateFile($file)
{
	$this->templateFile = $file;
}

public function render()
{
	$this->template->render($this->templateFile);
}
Alsatian
Člen | 164
+
0
-

@DavidMatějka bojuji, ale zřejmě úplně špatně.

forms/ContactForm.php

use Nette\Application\UI\Form;
use Nette\Security\User;
use Nette\Utils\DateTime;
use Nette\Utils\Image;
use Nette\Utils\Strings;
use App\Forms;
use Nette\Application\UI;
use Nette\Application\UI\Component;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;


class ContactForm extends UI\Control
{
	public function __construct()
	{
		parent::__construct();
	}

    public function render()
    {
        bdump('test');

        $template = $this->template;
        $template->setFile(__DIR__ . '/include.contact-form.latte');

        $template->render();
    }


	/**
	 * @return Form
	 */
    public function create()
	{
        $form = new Form;
        $form->addText('jmeno', 'Jméno:');
        $form->addText('telefon', 'Telefon:');
        $form->addSubmit('action', 'Odeslat');

        $form->onSuccess[] = function (Form $form, \stdClass $values) {
            // zde provedeme zpracování formuláře
        };

        return $form;
	}
}

/** rozhranní pro generovanou továrničku */
interface IContactFormFactory
{
    /** @return \ContactForm */
    function create();
}

V config.neon

services:
    - IContactFormFactory

BasePresener.php

/** @var \IContactFormFactory @inject */
public $contactFormFactory;

public function createComponentContactForm()
{
	$control = $this->contactFormFactory->create();
	return $control;
}

A PagePresenter.php

if(preg_match('/{{?kontaktni-formular}?}/', $pageHtmlText)) {
ob_start();
	$this->contactFormFactory->render(); // - vubec nevím, jak to zde mám vypsat...
$formTemplate = ob_get_clean();
...

Obdržím chybu: „Call to undefined method Container_461662e3df_IContactFormFactoryImpl81_IContactFormFactory::render()“

Pokud vykreslení mezi ob_start dám $this->contactFormFactory->create();

  • nevyhodí to chybu, ale formulář se nezobrazí. Debugováním zjistím, že se provede jenom __construct, ale už ne render nebo create (zkouším bdump s libovolným textem)
Martk
Člen | 655
+
0
-
$this['contactForm']->render();

a nebo když nechceš použít tu komponentu tak:

$this->addComponent('dynamicContactForm', $form = $this->contactFormFactory->create());
$form->render();
Alsatian
Člen | 164
+
0
-

@DavidMatějka @Martk
Díky Vám oběma jsem zase kousek pokročil.
Mám tedy továrničku a snažím se pomocí ní vykreslit formulář. Zde narážím na další chybu.
Formulář vykresluji (v šablonce továrnou vytvořené komponenty) pomocí:

{control contactForm}

což hodí chybu „Component with name ‚contactForm‘ does not exist.“
Nemá tam být něco jako self nebo jak se vlastně odkážu na vykreslení formuláře, který je v továrničce, který vykresluje svou šablonu? :) No nejde mi to do hlavy :D

Editoval Alsatian (5. 2. 2019 19:22)

Alsatian
Člen | 164
+
0
-

Pomalu na to přicházím… Formulář nemůže být v komponentě v create(), ale ve vlastní fci:

protected function createComponentMyForm()
    {
        $form = new Form;
        $form->addText('jmeno', 'Jméno:');
        $form->addText('telefon', 'Telefon:');
        $form->addSubmit('action', 'Odeslat');

        $form->onSuccess[] = function (Form $form, \stdClass $values) {
            // zde provedeme zpracování formuláře
        };

        return $form;
    }

a vykreslí se pomocí

{control myForm}

Editoval Alsatian (5. 2. 2019 19:39)

Alsatian
Člen | 164
+
0
-

@DavidMatějka – formulář už se mi vykresluje. Nyní tedy jako generovaný z továrničky.
Výsledek je ale stejný. Nelze invalidovat snippet uvnitř šablony. Zajímavé je, že pokud form nevypíšu ručně, ale pomocí {control myForm}, tak chybu ve formuláři při odesílání to vypíše červeně nad formulářem.

David Matějka
Moderator | 6445
+
0
-

@Alsatian muzes prosim ukazat vsechen relevantni kod, jak jej mas ted? komponentu, sablonu a presenter

Alsatian
Člen | 164
+
0
-

@DavidMatějka – ahoj, jelikož si stále nevím rady, udělal jsem si čistý projekt a posílám z něj tedy ukázku. Jenom připomínám, že se mi nedaří zobrazit (invalidovat) obsah uvnitř formuláře, který je generovaný továrničkou a vykreslený ruční šablonkou. Snažím se zde napsat něco jako „Hotovo, formulář byl odeslán“.

BasePresenter.php

<?php

namespace App\Presenters;

use Nette;
use Nette\Application\UI\Form;


abstract class BasePresenter extends Nette\Application\UI\Presenter
{

    public function __construct() {
        parent::__construct();
    }

    public function createComponentContactForm()
    {
        $control = new \App\Components\ContactForm();
        $control->setTemplateFile(__DIR__ . '/../components/ContactForm.latte');
        return $control;
    }

}

@layout.latte

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>{ifset title}{include title|stripHtml} | {/ifset}Nette Sandbox</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <link rel="stylesheet" href="{$basePath}/css/style.css">
	{block head}{/block}
</head>

<body>
    {snippet flashMessage}
        <div n:foreach="$flashes as $flash" n:class="flash, $flash->type">{$flash->message}</div>
    {/snippet}

    <div class="container py-5">
        {snippet content}
            {control contactForm}
        {/snippet}
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
    <script type="text/javascript" src="{$basePath}/js/nette/netteForms.js"></script>
    <script type="text/javascript" src="{$basePath}/js/nette/nette.ajax-2.3.0.js"></script>
    <script type="text/javascript" src="{$basePath}/js/main.js"></script>
</body>
</html>

components/ContactForm.php – moje továrnička na formulář

<?php

namespace App\Components;

use Nette\Application\UI\Form;

class ContactForm extends \Nette\Application\UI\Control
{

    private $templateFile;

    public function setTemplateFile($file)
    {
        $this->templateFile = $file;
    }

    public function render()
    {
        $this->template->render($this->templateFile);
    }

    public function createComponentMyForm()
    {
        $form = new Form;
        $form->getElementPrototype()->class("mbr-form ajax");

        $form->addText('jmeno', 'Jméno')
                ->setRequired('Zadejte své jméno')
                ->setHtmlAttribute('placeholder', 'Jméno');

        $form->addSubmit('send', 'Odeslat');

        $form->onSuccess[] = [$this, 'formSucceeded'];

        return $form;
    }

    public function formSucceeded($form, $values)
    {
        $p = $form->getPresenter();

        //...

        if($p->isAjax()) {
            $message = 'Odesláno.';

            $form->reset();
            $p->template->formMessage = $message; // tohle nedovedu invalidovat uvnitr komponenty - sablona

            $p->redrawControl('content');
            $p->redrawControl('myForm');
            $p->redrawControl('formMessage');
            $p->redrawControl('formError');
            $p->redrawControl('formBody');

        }
        else {
            $p->flashMessage($message);
            $p->redirect('this');
        }
    }
}


interface IContactFormFactory
{
    /** @return ContactForm */
    public function create();
}

components/ContactForm.latte – šablonka pro komponentu formuláře

{snippet myForm}
    {snippet formMessage}
        {ifset $formMessage}
            <div class="alert alert-success" role="alert">
                <big>{$formMessage}</big>
            </div>
        {/ifset}
    {/snippet}

    <form n:name="myForm">
        {snippet formError}
            {ifset $form}
                <div n:if="$form->hasErrors()" class="alert alert-danger" role="alert">
                    <li n:foreach="$form->ownErrors as $error">{$error}</li>
                </div>
            {/ifset}
        {/snippet}

        {snippet formBody}
            <div class="row">
                <div class="col-12 col-lg-6">
                    <div class="form-group">
                        <input n:name="jmeno" class="form-control">
                    </div>
                </div>
            </div>

            <button n:name="send" class="btn btn-primary px-4 py-2">Odeslat</button>
        {/snippet}
    </form>
{/snippet}

Homepage presenter a jeho default šablonka je prázdná. Prozatím se snažím vykreslit formulář přímo v @layout.latte.
Moc děkuji, že se mnou ztrácíš čas :)
PS: Umístil jsem projekt na cloud, jestli to třeba nebude jednodušší :)
https://drive.google.com/…xPYxy9m/view?…

Editoval Alsatian (7. 2. 2019 9:04)

David Matějka
Moderator | 6445
+
+1
-

máš to skoro správně, jen je potřeba

  • invalidovat snippet na komponentě, ne na presenteru
  • a stejně tak tu message nastavovat na šablonu komponenty

v tom bloku if isAjax dej:

$message = 'Odesláno.';
$form->reset();
$this->template->formMessage = $message;
$this->redrawControl('formMessage');
Alsatian
Člen | 164
+
0
-

@DavidMatějka – ty jsi poklad! Nevím, jestli jsou někde tyto vědomosti k dipozici, nic jsem nedohledal. A tak nějak mi něco říkalo, že poslání message bude muset být jinak. Díky moc. Jdu to doplnit do mého projektu.

Měl jsem za to, že když chci nějaký prvek invalidovat, musím invalidovat snippety postupně. Proto ta moje posloupnost. Stačí zřejmě jenom onen konkrétní, kdy pokud se vkládá šablonou, je potřeba tuto ještě obalit pomocí snippetArea, ale stále volám jenom konkrétní (cílový) snippet. Hurá :)

Prosím tě, a mám se i na isAjax ptát pomocí $this->isAjax() nebo pomocí $p = $form->getPresenter(); $p->isAjax(); ?

Editoval Alsatian (7. 2. 2019 9:40)

David Matějka
Moderator | 6445
+
0
-

@Alsatian nejake informace o snippetech najdes v moji prednasce

Alsatian
Člen | 164
+
0
-

@DavidMatějka – doplnil jsem svůj nynější čistý projekt o vykreslení formuláře ne z @template.latte, ale za pomocí $this[‚contactForm‘]->render(); a obdržel Warning „end() expects parameter 1 to be array, null given“

Chyba:
File: …\latte\components-ContactForm.latte–3b9ddb9819.php:111

102:        function blockFormBody($_args)
103:        {
104:            extract($_args);
105:            $this->global->snippetDriver->enter("formBody", "static");
106:    ?>
107:                <div class="row">
108:                    <div class="col-12 col-lg-6">
109:                        <div class="form-group">
110:                            <input class="form-control"<?php
111:            $_input = end($this->global->formsStack)["jmeno"];
112:            echo $_input->getControlPart()->addAttributes(array (
113:            'class' => NULL,
114:            ))->attributes() ?>>
115:                        </div>

presenters/HomepagePresenter.php

<?php

namespace App\Presenters;


class HomepagePresenter extends BasePresenter
{
	public function renderDefault()
	{
		$pageHtmlText = "Jak se {kontaktni-formular} vede?";

        if(preg_match('/{{?kontaktni-formular}?}/', $pageHtmlText)) {
            ob_start();
                $this['contactForm']->render();
            $formTemplate = ob_get_clean();

            $pageHtmlText = preg_replace('/{{?kontaktni-formular}?}/', (string) $formTemplate, $pageHtmlText, 1);
        }

        $this->template->html_text = $pageHtmlText;
	}
}

Homepage/default.latte

{block content}

    <div class="container">
        <h1 n:block=title>Test projekt</h1>
        <br>

        {$html_text|noescape}

    </div>

{/block}

Editoval Alsatian (7. 2. 2019 10:02)

David Matějka
Moderator | 6445
+
0
-

pokud invalidujes cast formulare, kde jsou nejake prvky a inputy, tak musis

  1. cely formular obalit do snippetArea
  2. cast s inputem, ktery chces prekreslit, obalit do normalniho snippetu
  3. invalidovat oba snippety

(nebo pripadne cely form obalit jednim snippetem a invalidovat pouze ten)

Alsatian
Člen | 164
+
0
-

@DavidMatějka – zkusil jsem a vlastní zprávu takto zobrazím. První krok mám tedy splněn. Ještě se mi nevypisují chyby chyby. Zdá se mi, že nefunguje ono {ifset $form} v šabloně viz, protože i když umístím blabla hned za ifset $form, nic se nezobrazí. Invaliduji úplně stejně jako zprávu po odeslání.

<form n:name="myForm">
            {snippet formError}
                {ifset $form}
                    <div n:if="$form->hasErrors()" class="alert alert-danger" role="alert">
                        <li n:foreach="$form->ownErrors as $error">{$error}</li>
                    </div>
                {/ifset}
            {/snippet}
...

Editoval Alsatian (7. 2. 2019 11:21)

MajklNajt
Člen | 471
+
0
-

skús tam pridať miesto {ifset $form} toto {var $form = $control["myForm"]}

<form n:name="myForm">
	{snippet formError}
		{var $form = $control["myForm"]}
		<div n:if="$form->hasErrors()" class="alert alert-danger" role="alert">
			<li n:foreach="$form->ownErrors as $error">{$error}</li>
		</div>
	{/snippet}
</form>
David Matějka
Moderator | 6445
+
+1
-

tim snippetem se ztraceji lokalni promenne, budes asi muset pod snippet muset pridat neco jako:

{var $form = end($this->global->formsStack)}

pripadne druhou moznosti je dat do render metody te komponenty:

$this->template->form = $this['myForm'];
Alsatian
Člen | 164
+
0
-

Děcka, to je složité, ale funguje ;)
Zvolil jsem doplnění

$this->template->form = $this['myForm'];

Snad to pomůže i někomu dalšímu. Ještě jednou moc děkuji.