Looking to extend CForm but how?

Hello,

I am currently switching over to CForm to display all of my forms (great feature by the way) but because I am using Twitter bootstrap all of my forms are looking a bit out of wack. Let me try to explain a bit more and my thought process.

Here is what is being outputting by CForm…


<div class="row field_Name">

	<label for="Tenant_Name" class="required">Name <span class="required">*</span></label>

	<input name="Tenant[Name]" id="Tenant_Name" type="text" maxlength="50">

</div>

This is kinda what I need CForm to output in order to be compatible with Twitter Bootstrap 2.0…


<div class="control-group">

	<label class="control-label" for="LoginForm_username">Email <span class="required">*</span></label>

	<div class="controls">

		<input name="LoginForm[username]" id="LoginForm_username" type="text">

		<div class="errorMessage" id="LoginForm_username_em_" style="display:none"></div>

	</div>

</div>

So I think I need to create a controller and extend CForm (note: I have already done) then copy/paste in the "render" methods and switch them up to display the correct markup. But here is my problem… currently to render a form I do this, "echo $form->render();" but how do I do this with a component? Note: I have already added the component to my config file and its route is Yii::app()->form. Or am I doing this totally wrong?

Any help would be VERY VERY VERY much appreciated.

Hi,

First: There is an extension available at http://www.yiiframework.com/extension/bootstrap, but I never used it and it may not have any CForm support. If the extension can’t be used then it should work that way:


class BootstrapForm extends CForm

{

    public $inputElementClass="BootstrapInputElement";

}


class BootstrapInputElement extends CFormInputElement

{

    public static $coreTypes=array(

        'text'=>'BStextField'

        .....other elements here......

    );


    public $layout="<div>BOOTSTRAP ME<div class="foo">{{label}}</div><div class="bar">{{input}}</div></div>"; //specify how label, hint and input are rendered


    //needs to be overridden to make use of BootstrapHtml instead of CHtml. Also take a look at renderLabel and renderHint for the same reason

    public function renderInput()

    {

    if(isset(self::$coreTypes[$this->type]))

    {

        $method=self::$coreTypes[$this->type];

        if(strpos($method,'List')!==false)

            return BootstrapHtml::$method($this->getParent()->getModel(), $this->name, $this->items, $this->attributes);

        else

            return BootstrapHtml::$method($this->getParent()->getModel(), $this->name, $this->attributes);

    }

    else

    {

        $attributes=$this->attributes;

        $attributes['model']=$this->getParent()->getModel();

        $attributes['attribute']=$this->name;

        ob_start();

        $this->getParent()->getOwner()->widget($this->type, $attributes);

        return ob_get_clean();

    }

    }

}




the placeholders (like {{label}}) will normally be rendered using a CHtml method (CHtml::label, CHtml::textField etc.).

The problems arise when you need a special formatting (like with checkboxlists etc) and the rendered CHtml element just doesn’t render like that and can’t be configured properly. Then you basically have two options: The not so elegant method is to override the render methods in your new BootstrapInputElement and create some logic the does that for you or you extend CHtml and create a BootstrapHtml class where you create methods for the needed elements like I did above in the renderInput() method. By using the coreTypes array in your new BootstrapInputElement class you actually map the type to the BootsrapHtml method.


class BootstrapHtml extends CHtml

{

    public static function BStextField($name,$value='',$htmlOptions=array())

    {

        return '<div class="foo">'.CHtml::textField(($name,$value='',$htmlOptions=array()).'</div>';

    }

}

So if you specify "text" as element type in your cform config it would create a new BStextField as you mapped it to the BStextField method.


'elements'=>array(

     'username'=>array('type'=>'text', 'maxlength'=>80),

 )

I know it looks really complex, but for the most part it is copy and paste. This might give you an idea

Hope it helps,

Hannes

P.S.: I know that a lot can be done using a view rendering each CForm element via $element->render() and taking care of the formatting in the view itself but as I mentioned: As CHtml is used to create each element you are never in full charge of how the created element itself will look like and the above method has one additional benefit: You can now use BootstrapHtml::BStext to render such an element wherever you want, even if you don’t use CForm

Yeah I need to use CForm because for my project I have about 50 or so forms that need to be created. Some simple and some complicated. Mostly complicated :( but I like how I can clean out my views and keep the form logic elsewhere.

Ok, so here is what I have done…

I made 2 files in my /protected/components/

One file with "class BootstrapForm extends CForm" and the other with "class BootstrapHtml extends CHtml"

I then add those to my config and preload them?

But it get an error…


Missing argument 1 for CForm::__construct(), called in /srv/www/www.powertrade.com/public_html/framework/YiiBase.php on line 219 and defined

No, you don’t need to add them to your config. You just have to tell your new BootstrapForm to use your custom input element class by setting the public property $inputElementClass like:


class BootstrapForm extends CForm

{

    public $inputElementClass="BootstrapInputElement";

}

This tells the form to use the BootsrapInputElement class to render input elements. You have to create that one too like i mentioned above (see the BootstrapInputElement class in my example)

Or if you want to be really lazy, you could always render elements bare and include the styling in the css where bootstrap is lacking.


$html=preg_replace('/class=".*?"/', '', $form->render());

$html=preg_replace('/id=".*?"/', '', $html);

echo($html);



:slight_smile: Timo