how to pass data from createComponent totemplate in Version 3.0 ?

7 months ago

thcom
Member | 55
+
0
-

Hi, simple form example

presenter:

<?php
	protected function createComponentTestForm()	{
		$this->template->test = 'KozelX';
		$testForm = new UI\Form;
		$testForm->addText('osel', '');
		$testForm ->addSubmit('save', 'uložit');
	        $testForm->onSuccess[] = [$this, 'TestFormSucceeded'];
		return $testForm ;
	}
?>

template:

{block content}

DUMPTEST >{$test}<

{control testForm }

{/block}

in version 3.0 i get
Notice – Undefined variable: test

in version 2.4 i can see KozelX text

please help me, how to pass variables to template

many thanks

7 months ago

teekey99
Member | 43
+
0
-

You're trying to render $test variable before it is assigned to template. It's because your createComponentTestForm method is called with {control testForm} in the template, which happens after DUMPTEST >{$test}<.

Assign the $test variable in action or render method, not in createComponent.

7 months ago

thcom
Member | 55
+
0
-

thank you
in version 2.4 it was working

and i need pass variable from create component, because i create special desing of form save it to an array and i render page design from this array

thank You

7 months ago

Mabar
Member | 286
+
+1
-

Imho nothing changed in 3.0 in components creation. createComponent* method is called first time it's used. When form is sent then latest moment is in handle* call stage, when form is rendered it's in template. In case it's first time used in template is template variable assignment done too late. That's why you should do only stuff directly related to component in createComponent* methods. But you can also initialize component sooner, just by calling $this['testForm'], e.g. in beforeRender()

7 months ago

teekey99
Member | 43
+
0
-

thcom wrote:

thank you
in version 2.4 it was working

and i need pass variable from create component, because i create special desing of form save it to an array and i render page design from this array

thank You

In that case I would suggest wrapping this all in a component that can handle both the form and the design you need for it.

7 months ago

thcom
Member | 55
+
0
-

hi, i try to study
pla.nette.org/cs/best-practise-formulare-jako-komponenty

i am able to create a form with a factory

but when i tray wrap the form in UI\Control i have a problem with attaching this component to presenter

i get message Component of type ‘App\Forms\TranslateFormFactory’ is not attached to ‘Nette\Application\UI\Presenter’

component:

<?php
class TranslateFormFactory extends UI\Control	{

	private $database;

	public function __construct(Nette\Database\Connection $database)	{
		$this->database = $database;
	}

	protected function attached($presenter): void	{

		parent::attached($presenter);

	}

	public function create()	{
		$form = new UI\Form;
		// mohu použít $this->database

		$form->addText('vosel', 'KozelX');
		$form->addSubmit('send', 'Odeslat');
		$form->onSuccess[] = [$this, 'processForm'];

		$this->template->kozel = 'Vosel';


		return $form;
	}

	public function processForm($form)	{

		dump($form->values); exit;

	}
}


/** rozhranní pro generovanou továrničku */
interface ITranslateFormFactory	{

	/** @return TranslateFormFactory */
	function create();
}
?>

and presenter:

<?php
final class HomepagePresenter extends BasePresenter	{

	/** @var Forms\TranslateFormFactory @inject */
	public $translateFormFactory;

	protected function createComponentTranslateForm()	{
		$control = $this->translateFormFactory->create();
		$control->onSuccess[] = function (UI\Form $form) {
			$this->redirect('this');
		};

		return $control;
	}
}
?>

i am not sure, if i undestand the section with onCategorySave() method

thank you !!

7 months ago

CZechBoY
Member | 3572
+
0
-

Rename create in form factory (TranslateFormFactory) to createComponentForm and rename the class itself to not contain factory – as it is not factory but control/component (this is not required by nette but is better to undestand what is that class).
Also remove attached method (it is useless).

In presenter inject that interface generated factory and call that in createComponentTranslateForm. You can find your form in $control['form'], where form is the name of component in TranslateFormControl (the text after createComponentForm).

Sorry for not pasting code, i’m on mobile :-(.

7 months ago

teekey99
Member | 43
+
0
-

You're probably misunderstanding the concepts of Control components and their factory interfaces.

Control component allows you to create re-usable parts of your application. Same as presenter, it provides you with core functionalities of nette/component-model. Each presenter is basically a component too. Control is an isolated pice of UI, that can be easily rendered in any presenter. The factory interface takes the role of a service in Dependency Injection Container where Nette uses its create method to create an instance of your component.

So if I have a component MyComponent and a factory interface MyComponentFactory, then Nette de-facto calls new MyComponent() while implementing the factory's create method. The component class itself don't have to have the create method.

Then, inside the component, you can create and attach other components (also forms), make redirects, create and handle signals etc.

Reviewing your code, it should look something like this:

FormWrapper.php

class FormWrapper extends UI\Control
{

	private $database;

	public function __construct(Nette\Database\Connection $database)
	{
		$this->database = $database;
	}

	public function render()
	{
		$this->template->kozel = 'Vosel';
		$this->template->render(__DIR__ . '/templates/formWrapper.latte')
	}

	protected function createComponentForm()
	{
		$form = new UI\Form;
		$form->addText('vosel', 'KozelX');
		$form->addSubmit('send', 'Odeslat');
		$form->onSuccess[] = [$this, 'processForm'];
		return $form;
	}

	public function processForm($form)
	{
		bdump($form->values);
		$this->presenter->redirect('this');
	}

}

templates/formWrapper.latte

In component template, you render UI\Form.

{control form}

FormWrapperFactory.php

interface FormWrapperFactory
{

	/**
	 * @return FormWrapper
	 */
	public function create();

}

HomepagePresenter.php

final class HomepagePresenter extends BasePresenter
{

	/** @var FormWrapperFactory @inject */
	public $formWrapperFactory;

	protected function createComponentFormWrapper()
	{
		return $this->formWrapperFactory->create();
	}

}

templates/Homepage/default.latte

In presenter template you render UI\Control which wraps and renders UI\Form created inside of it.

{block content}
{control formWrapper}

For further info, please refer to components docs.

Last edited by teekey99 (2019-12-19 23:47)

7 months ago

teekey99
Member | 43
+
0
-

Oh BTW, since you're asking about Nette 3.0, it supports latest version of PHP so you can make use of advanced type hints (typed properties, strict return types etc.). Much better than just annotations, meaning you can for example do:

interface MyComponentFactory
{

	public function create(): MyComponent;
}

Last edited by teekey99 (2019-12-19 23:52)

7 months ago

thcom
Member | 55
+
0
-

perfect !

many thanks for ypour help

now i have a little knowledge about factories i try some modifications and it clear

but last (I HOPE) questuion is
how to pass parameters to a form ?

for example i have form with some table rows nazev_cz and nazev_en
and i need to call form with parameter $tb_name

in template
{control translateForm, $tb_name}

is thos right way and where can i this parameter catch ?

thank you !!

7 months ago

CZechBoY
Member | 3572
+
0
-

Better is to create component with this parameter (in presenter/component) rather than call in parametrised in template.
But if you really want it you can call it in template as you wrote ({control translateForm, $tb_name}) and in FormWrapper::render accept this parameter:

	public function render($tb_name)
	{
		$this->template->kozel = 'Vosel';
		$this->template->tb_name = $tb_name;
		$this->template->render(__DIR__ . '/templates/formWrapper.latte');
	}

btw name of that parameter in method render is on you, Nette pass it as positional parameters (not named).

Last edited by CZechBoY (2019-12-20 15:42)