Different form formatter for embeded form? - php

I'm trying to change the form formatter of the embeded form. Is it possible to approach something like this?
class sfOuterForm extends sfForm {
public function configure()
{
$innerForm = new sfForm();
$this->embedForm('inner', $innerForm);
$this->getWidgetSchema()->setFormFormatter('list');
$this->getEmbeddedForm('inner')->getWidgetSchema()->setFormFormatterName('table');
}
}
i'm expecting the following:
echo (new sfOuterForm())
outputs:
<li><label>Outer Label</label><input type="text" /></li>
<li>
<table>
<tr><td><label>Inner Label</label></td><td><input type="text" /></td></tr>
</table>
</li>

Once a form is embedded, it's original widget schema and validator schema do nothing - they've been merged into the top level schemas. Thus, you need to set the form formatter before embedding:
$this->getWidgetSchema()->setFormFormatter('list');
$innerForm = new sfForm();
$innerForm->getWidgetSchema()->setFormFormatterName('table');
$this->embedForm('inner', $innerForm);
It's worth a look into sfForm::embedForm to see what's going on internally.

I'll answer my question by myself :)
The problem arised when i tried to change formatter for relation's embedded forms. I solved this as follows:
class sfOuterForm extends sfForm {
public function configure()
{
$innerForm = new sfForm();
$this->embedRelation('relationName');
$this->getWidgetSchema()->setFormFormatter('list');
$this->getEmbeddedForm('relationName')->getWidgetSchema()->setDefaultFormFormatterName('table');
}
}
Hope this will help someone :)

Related

CakePHP: How to use a view element inside of a controller

I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php

Zend 2.0: 500 error when creating form element

I am learning Zend Framework (2.0), and I'm stuck at creating forms.
Here is the code I used (Inside a controller):
use Zend\Form\Element;
use Zend\Form\Form;
...
public function indexAction()
{
$element = new Element\Text('name');
//Nothing else
}
It always gives a 505 error, but if I comment out the line "$element ..." then it works (so the problem must be there).
Can someone point out what I have overlooked?
Also, as I see it, there are too many ways to create a form. For example, I have tried:
private function getSignupForm() {
//Create Form
$form = new Zend_Form();
$form->setAction('success');
$form->setMethod('post');
$form->setAttrib('sitename', 'mysite');
//Add Elements
//Create Username Field.
$form->addElement('text', 'username');
$usernameElement = $form->getElement('username');
$usernameElement->setLabel('Username:');
$usernameElement->setOrder(1)->setRequired(true);
return $form;
}
This way, it worked, but that is not the way the tutorial says link. So in which way should I should write it?
Thanks.

Zend Forms email element changing to text

I'm trying to create an input of type email in Zend Forms but I cannot accomplish the desired result.
I've created a custom class:
namespace AAA\Forms\Elements;
class Email extends \Zend_Form_Element_Text
{
function __construct($name, $label, $required)
{
parent::__construct($name);
$this->setAttrib('type', 'email');
$this->setLabel($label)->setRequired($required);
}
}
And then I'm using it like this:
class Application_Form_Register extends Zend_Form
{
public function init()
{
// …
$email = new AAA\Forms\Elements\Email('email', 'E-mail:', true);
$this->addElement($email);
// …
}
}
And what I get is:
<input type="text" name="email" id="email" value="" type="email">
Note the two type parameters.
I have set HTML5 doctype ($documentType = new Zend_View_Helper_Doctype(); $documentType->doctype('HTML5'); in Bootstrap.php) and I have also tried this:
function __construct($name, $label, $required)
{
$options = array();
$options['type'] = 'email';
parent::__construct($name, $options);
// …
}
I've read Zend HTML5 Form element types but none of the answers work. I've tried also Glitch library but the result is the same.
Anyone knows how to force Zend Framework to use HTML5 form controls?
You will need to implement your own view helper for rendering the email form element as well.
Zend_Form_Element_Text uses the formText view helper (Zend/View/Helper/FormText.php) to render the HTML input and it is hard coded to output <input type="text" when rendering the element.
Two possible ways to handle this would be to:
Remove the ViewHelper decorator from the element and use the ViewScript helper to render the element.
Make your own view helper formEmail almost identical to formText except that it outputs type="email"; and set the public $helper property in your Element class to formEmail. You will have to register the path to the formEmail helper using Zend_View::addHelperPath() so it will be found by the ViewHelper decorator.

ZendFramework - How to add ->HeadScript() from Controllers?

I have a case where i need to add the Javascript from controller to the Layout where it has already HeadScript();
How to do that from controller?
e.g: $this->view->HeadScript()->appendScript();
This is controller: Both does not apply.
class RouterController extends Zend_Controller_Action
{
public function init()
{
$this->view->HeadScript()->appendFile('/js/test.js')->setIndent(8);
$this->view->HeadScript( Zend_View_Helper_HeadScript::FILE, '/js/test.js' );
}
}
This is the view file: index.phtml
<?//$this->HeadScript()->appendFile('/js/test.js')->setIndent(8);?>
If i uncomment in view it works but not in Controller. I want to apply this from controller how now?
$this->view->headScript()->appendFile('/path/to/file.js');
<?//$this->HeadScript()->appendFile('/js/test.js')->setIndent(8);//Your question ?>
$this->view->headScript()->appendFile('/path/to/file.js');//Alex Howansky's answer
There slightly different. :)
I got this to work from the preDispatch method in a controller, remember you have to pass layout changes before headers are passed.
public function preDispatch() {
parent::preDispatch();
$layout = new Zend_Layout();
$layout->getView()->headScript()->appendScript('/javascript/form.js', 'text/javascript');
}
you still have to have the headScript placeholder in your layout.
$this->view->HeadScript( Zend_View_Helper_HeadScript::FILE, '/path/to/file.js' );
or
$this->view->HeadScript( Zend_View_Helper_HeadScript::SCRIPT, 'js code' );
The same for $this->view->InlineScript().
$headScript = $this->getServiceLocator()->get('viewhelpermanager')->get('headScript');
$headScript->prependFile($this->getSystemBaseUrl()."js/front_end/lib/jQuery/jquery-2.2.3.min.js","text/javascript");
In case if its still useful to someone, you need to include following in layout script file:
<?php echo $this->headScript(); ?>
Reference: https://framework.zend.com/manual/1.11/en/zend.view.helpers.html#zend.view.helpers.initial.headscript

Symfony Form: Set default/value on error?

My crazy designer would like the message "Required" displaying (in red) inside a field if the form has been submitted and it is invalid because of an empty field.
The form in question is a login prompt and I'm using a custom class that extends sfGuardFormSignin
I've managed to set the value and add a class with..
$this->widgetSchema['username']->setAttribute('class','red');
$this->widgetSchema['username']->setDefault('Required');
..but how do I do this only when the username field is invalid and because of the Required error?
I assume it's the same for the password field?
Many thanks in advance
EDIT:
Thanks for the advice greg0ire. I've had a play with that but the formatRow method of sfWidgetFormSchemaFormatter doesn't seem to be getting hit. Is this because my form extends sfGuardFormSignin and using the sfGuardAuth plugin?
class FrontendsfGuardFormSignin extends sfGuardFormSignin
{
public function configure()
{
parent::configure();
// This works!
$this->widgetSchema['username']->setLabel('Email');
// I copied this from the link you pasted
$decorator = new myWidgetFormSchemaFormatterCustom($this->getWidgetSchema());
$this->widgetSchema->addFormFormatter('custom', $decorator);
$this->widgetSchema->setFormFormatterName('custom');
}
}
/lib/widget/myWidgetFormSchemaFormatterCustom.class.php
class myWidgetFormSchemaFormatterCustom extends sfWidgetFormSchemaFormatter
{
public function __construct(sfWidgetFormSchema $widgetSchema)
{
parent::__construct($widgetSchema);
}
public function formatRow($label, $field, $errors = array(), $help = '', $hiddenFields = null)
{
// Nothing happens!?
var_dump($errors);
die();
parent::formatRow($label, $field, $errors, $help, $hiddenFields);
}
}
$widget->render(array('value' => $widget->getError()));
Designers have such crazy ideas...
You'll have to write a custom schema formatter to do this. You'll probably have to override the formatRow() method to achieve this.
Analyse the $errors array argument of this method, and if you spot the "Required" error in it, then do your special stuff. You won't need to use the code you posted in your question.

Categories