In developing a site that can have multiple front-end themes, I am looking for a way to allow users on the back-end to manually inject code into the head and body. Two use cases are as follows:
Case 1 - Styles
In the back-end, the user selects a theme.
User likes the theme but would like to make links a different color.
Rather than copy and modify the theme, user can set custom code to execute at end of <head> tag.
Case 2 - Scripts
User wants to add custom JavaScript to end of document but requires an additional JavaScript library as well.
Rather than copy and modify the theme, user can set custom code to execute at and of <body> tag.
I understand that both of these specific cases could be accomplished (in part) with the use of registerCss and registerJs but those automatically wrap whatever is passed to them in <style> or <script> tags. I am hoping there is a way to just directly inject whatever is indicated directly into the head() or endBody() methods. The reason behind this is that I do not wish to limit what the user can inject (perhaps a script tag is needed in the head).
Currently, I am just storing the code-to-be-added in params then manually including them in each theme as follows:
<?php $this->endBody() ?>
<?= $this->params['theme_include_body_end'] ?>
This is undesirable as it can easily be forgotten when creating the theme. I would like to find a way to append my param value to the endBody() call automatically so whenever endBody() is called, my code is included (same for the head() call).
Yii2 already provide this functionality in View Class by using Block Widget
you need 2 simple steps:
1- (in required View file): in any given view
<?php $this->beginBlock('block1'); ?>
...content of block1...
<?php $this->endBlock(); ?>
...
<?php $this->beginBlock('block3'); ?>
...content of block3...
<?php $this->endBlock(); ?>
2- (in layout): define block name and its place in the layout page
...
<?php if (isset($this->blocks['block1'])): ?>
<?= $this->blocks['block1'] ?>
<?php else: ?>
... default content for block1 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block2'])): ?>
<?= $this->blocks['block2'] ?>
<?php else: ?>
... default content for block2 ...
<?php endif; ?>
...
<?php if (isset($this->blocks['block3'])): ?>
<?= $this->blocks['block3'] ?>
<?php else: ?>
... default content for block3 ...
<?php endif; ?>
...
Referance: Yii2 Guide
http://www.yiiframework.com/doc-2.0/guide-structure-views.html#using-blocks
I hope this will help someone. Thank you.
You can use own View component that overrides methods renderHeadHtml() and renderBodyEndHtml(). In these methods can be injected necessary code as you need:
namespace common/components;
class View extends \yii\web\View {
/**
* #var string Content that should be injected to end of `<head>` tag
*/
public $injectToHead = '';
/**
* #var string Content that should be injected to end of `<body>` tag
*/
public $injectToBodyEnd = '';
/**
* #inheritdoc
*/
protected function renderHeadHtml()
{
return parent::renderHeadHtml() . $this->injectToHead;
}
/**
* #inheritdoc
*/
protected function renderBodyEndHtml($ajaxMode)
{
return parent::renderBodyEndHtml(ajaxMode) . $this->injectToBodyEnd;
}
}
In config file:
// ...
'components' => [
// ...
'view' => [
'class' => '\common\components\View',
]
]
Somewhere in controller code:
\Yii::$app->view->injectToHead = '...';
\Yii::$app->view->injectToBodyEnd = '...';
Another, perhaps more simple alternative depending on your use case is to use the view events. You can inject different items at different parts of the page. For example:
Yii::$app->view->on(View::EVENT_END_BODY, function () {
echo date('Y-m-d');
});
You can read more here: https://www.yiiframework.com/doc/guide/2.0/en/structure-views#view-events
Related
I'm using Yii2, activeForm and the Yii2 pjax widget to handle an search form with result list.
But for layout reason, I have to split this package to two parts: the search form, which should be located in the header of the page and the result listing, which should be placed in the middle of the page.
This means it's not possible to do something like that (pseudocode):
Pjax::begin
$form=>activeForm::begin..
ActiveForm::end();
ListView::widget...
Pjax::end()
What I need is placing the activeForm in the layout header, placing the ListView wiget in the center of the page and telling pjax: read the input from the form, load data from Server and replace the clist view.
Is that possible using the build-in functionality of the widgets?
I didn't get it working, maybe I misunderstood the basic concept?
Using gridview (or widget which use searchModel) you could use a separated model for modelSearch in header and show the resul in the gridview palced where you prefer
in this way you need only a pjax block
<?= $this->render('_search', ['model' => $searchModel]); ?>
<?php Pjax::begin(); ?>
<?= GridView::widget([
...
]); ?>
<?php Pjax::end(); ?>
for other soluzione not based on gridview and when you need multiple seprataed pjax block
You could try using named pjax block using a value for id
<?php \yii\widgets\Pjax::begin(['id'=>'pjax-block1']); ?>
....
....
<?php \yii\widgets\Pjax::end(); ?>
<?php \yii\widgets\Pjax::begin(['id'=>'pjax-block2']); ?>
....
....
<?php \yii\widgets\Pjax::end(); ?>
and if you need you could manage specific refresh using jquery
$.pjax.reload({container: '#pjax-block1', async: false});
$.pjax.reload({container: '#pjax-block2', async: false});
I want to add css class to body tag in yii2 advanced in frontend/views/layouts/main.php how can I do it?
You can do this dynamically like this:
<body class="<?= $this->context->bodyClass; ?>">
And in main Controller (all other controllers should extend this Controller) define property:
public $bodyClass;
or for default value:
public $bodyClass = 'custom-skin';
Ofc you can override this property in any extending controller by redefining it:
public $bodyClass = 'custom-skin-2';
In init:
public function init() {
parent::init();
$this->bodyClass = 'custom-skin-2';
}
In specific action:
public function actionView()
{
$this->bodyClass = 'custom-skin-3';
return $this->render('view');
}
You add your class simply to body tag
<body class="yourClass">
Another possible solution would be using the variable $params in your view.
Example
In your view you can define:
$this->params['bodyClass'] = 'yourclass';
And then, in your layout file, you'd go:
[.. head and other codes ..]
<body <? if(isset($this->params['bodyClass'])) echo 'class="' . $this->params['bodyClass'] . '"'; ?>>
<?php $this->beginBody() ?>
[.. rest of your code ..]
Notice that
I'm suggesting you to use the if so it only puts the class if it the $params['bodyClass'] is set in your view.
Also, you can use whatever name you want in the place of bodyClass.
This example will output <body class="yourclass">
Cheers.
You have on one page ActiveForm as master record, and grid as child records, how can i make relation between them. At create time there isn't id of master record! Any advice is wellcome! TIA. Asim
There is no need to use modal in order to add the child object. You can do it in one form. If you use a modal, you'll need to create the parent object first, and then allow this modal to be shown. In this way, your worries will be invalid.
If you want to add a parent and a child in one form, you can pass both objects to the view and then put their fields in the form. Then on form submit, you validate both objects, and then if everything is fine, save the parent, and then the child, assigning the parent id to the child. You can do this in a transaction so that if the child fails, the parent won't be recorded as well or vise versa.
Here is some code:
class YourController extends Controller
{
public function actionSomething()
{
$parent = new Parent();
$child = new Child();
$request = Yii::$app->request;
if ($parent->load($request->post('Parent')) && $child->load($request->post('Child'))) {
// Do validation and if everything is fine, then save the fields
Yii::$app->db->transaction(function() {
$parent->save(false);
$child->parent_id = $parent->id;
$child->save(false);
});
}
return $this->render('view', compact('parent', 'child'));
}
}
The view:
$form = ActiveForm::begin() ?>
<?= $form->field($parent, 'fieldA') ?>
<?= $form->field($parent, 'fieldB') ?>
<?= $form->field($child, 'fieldA') ?>
<?= $form->field($child, 'fieldB') ?>
...other input fields...
<?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
Did you get the idea? If you have questions or problems, please ask. Here is another example. This is with an update, but the idea is the same:
Getting Data for Multiple Models
My first time to use Twig template with CodeIgniter. I'm used to the default form functionality of the framework, but I was asked to try to render the form using Twig. I find the template engine to be nice and confusing at the same time. So that means, my controller would be very fat with code. But the main issue here is to render the form using twig.
Below is what I used to do when I want to render a form. View: TableSample.php
<?php
echo form_open("", array("name"=>"form_reg", "method"=>"post", "id"=>"form_reg"));
echo form_input("type"=>"text", "name"=>"fname", "value"=>set_value("fname"));
echo form_input("type"=>"text", "name"=>"lname", "value"=>set_value("lname"));
echo form_input("type"=>"text", "name"=>"emailaddress", "value"=>set_value("emailaddress"));
echo form_input("type"=>"submit", "name"=>"submit", "value"=>"Submit");
echo form_close();
?>
Controller: register.php
public function register (){
$this->load->view("TableSample");
if($this->input->post("submit")) {
/** retrieve input details, pass them as array to model, then redirect if registration is successful**/
}
}
But since I have to use Twig, things have been a little bit different.
public function register () {
$detail["form_open"] = form_open("", array("name"=>"form_reg", "method"=>"post", "id"=>"form_reg"));
$detail["form_input_name"] = form_input("type"=>"text", "name"=>"fname");
$detail["form_input_lname"] = form_input("type"=>"text", "name"=>"lname");
$detail["form_input_eadd"] = form_input("type"=>"text", "name"=>"email");
$detail["form_input_submit"] = form_input("type"=>"submit", "name"=>"submit", "value"=>"Submit");
$detail["form_close"] = form_close();
//codes for saving here
//call twig view
$this->twig->display("tableSample.html.twig", $detail);
}
tableSample.html.twig would be like this:
<html>
<head></head>
<body>
{{ form_open }} //will display form as a **String** and not THE **HTML** like this:
<form method="post" name="form_reg" id="form_reg"></form>
{{ form_close }}
</body>
</html>
I know I'm missing something, please point me to the right way of rendering this. Thank You!
ok, I think I got it. raw made it possible. Twig Raw Filter
I'm using Zend Framework and Zend_Form to render my form. But as I found it hard to customize it, I decided to print elements individually.
Problem is, I don't know how to print individual elements inside a display group. I know how to print my display groups (fieldsets) but I need to add something inside it (like a <div class="spacer"></div> to cancel the float:left.
Is there any way to display the group only without its content so I can print them individually myself?
Thank you for your help.
What you are looking for is the 'ViewScript' decorator. It allows you to form your html in any way you need. Here is a simple example of how it works:
The form, a simple search form:
<?php
class Application_Form_Search extends Zend_Form
{
public function init() {
// create new element
$query = $this->createElement('text', 'query');
// element options
$query->setLabel('Search Keywords');
$query->setAttribs(array('placeholder' => 'Query String',
'size' => 27,
));
// add the element to the form
$this->addElement($query);
//build submit button
$submit = $this->createElement('submit', 'search');
$submit->setLabel('Search Site');
$this->addElement($submit);
}
}
Next is the 'partial' this is the decorator, here is where you build the html how you want it:
<article class="search">
<!-- I get the action and method from the form but they were added in the controller -->
<form action="<?php echo $this->element->getAction() ?>"
method="<?php echo $this->element->getMethod() ?>">
<table>
<tr>
<!-- renderLabel() renders the Label decorator for the element
<th><?php echo $this->element->query->renderLabel() ?></th>
</tr>
<tr>
<!-- renderViewHelper() renders the actual input element, all decorators can be accessed this way -->
<td><?php echo $this->element->query->renderViewHelper() ?></td>
</tr>
<tr>
<!-- this line renders the submit element as a whole -->
<td><?php echo $this->element->search ?></td>
</tr>
</table>
</form>
</article>
and finally the controller code:
public function preDispatch() {
//I put this in the preDispatch method because I use it for every action and have it assigned to a placeholder.
//initiate form
$searchForm = new Application_Form_Search();
//set form action
$searchForm->setAction('/index/display');
//set label for submit button
$searchForm->search->setLabel('Search Collection');
//I add the decorator partial here. The partial .phtml lives under /views/scripts
$searchForm->setDecorators(array(
array('ViewScript', array(
'viewScript' => '_searchForm.phtml'
))
));
//assign the search form to the layout place holder
//substitute $this->view->form = $form; for a normal action/view
$this->_helper->layout()->search = $searchForm;
}
display this form in your view script with the normal <?php $this->form ?>.
You can use this method for any form you want to build with Zend_Form. So adding any element to your own fieldset would be simple.