ZF3: set terminal / render view without layout (Zend-Expressive) - zend-framework

I have installed the skelleton app from ZF3 'zend-expressive'. In the routing config I have configured a few routes. Some of these routes should return a response without the layout. In ZF2 I simply used the ViewModel on which you could call "setTerminal". But now the ViewModel is not directy available in the Action, as it is nested as private property of the ZendViewRenderer. I cannot figure out how I can set the terminal, so the output is rendered without layout.
I have tried various options in the routing configuration, such as adding keys 'terminal', 'terminate' and 'may_terminate' with value true. Also have tried to make a seperate Factory, but ended up with the same problem that I cannot reach the ViewModel.. It also did not work to make a seperate ViewModel in the Action, when I setTerminal on true, and pass the object as 2nd parameter in the 'render' method of the ZendViewRenderer object, it fails when passing 'renderModal' of the same object: "Cannot render; encountered a child marked terminal"..
There must be a simple configuration that I am overlooking, so my question is. Does anyone know how I can set the view on terminal?
Hope that I explained my problem well. Many thanks in advance.

My solution!
Yes! I found a "solution". Instead of pushing into terminal setting of the ViewModal, I have implemented a new layout called "layout/terminal". This layout only outputs the variable $content. Reference: https://github.com/zendframework/zend-expressive/issues/360
To use this layout you should add a new Factory into the configuration.
<?php
namespace Factory;
use Interop\Container\ContainerInterface;
use Zend\Expressive\Template\TemplateRendererInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\View\Model\ViewModel;
class RenderWithoutTemplate implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$template = $container->has(TemplateRendererInterface::class)
? $container->get(TemplateRendererInterface::class)
: null;
$r = new \ReflectionClass($template);
$view = new ViewModel();
$view->setTerminal(true); // Does not affect any render behaviour (?)
$view->setTemplate('layout/terminal');
$prop = $r->getProperty('layout');
$prop->setAccessible(true);
$prop->setValue($template, $view);
return $template;
}
}

public function indexAction(){
return $this->getResponse();
}

Related

How to Set View Model Class Programmatically when Creating Components

I am experimenting with ZK framework and look for ways to load a zul-defined view programmatically via a utility class that uses Executions to load a view definition programmatically.
I do however fail to find a way to set the view model class programmatically, which would be very convenient and would allow some very simple way of reusing a view with different view model classes.
I.e. the code to load the view looks like this:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
I thought about "forcing" the view model by setting annotations programmatically on the top component info from the page definition, like so:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
if (!page.getChildren().isEmpty()) {
ComponentInfo top = (ComponentInfo) page.getChildren().get(0);
AnnotationMap annotationMap = top.getAnnotationMap();
String viewModel = "viewModel";
if (annotationMap==null || !annotationMap.getAnnotatedProperties().contains(viewModel)) {
// no view model set on top declaration,
// force ours
Map<String,String[]> id = new HashMap<>();
id.put(null, new String[]{"vm"});
top.addAnnotation("viewModel","id",id, null);
Map<String,String[]> init = new HashMap<>();
init.put(null, new String[]{String.format("%s", modelClz.getName())});
top.addAnnotation("viewModel","init",init, null);
top.enableBindingAnnotation();
}
}
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
This did not work however. Maybe it was too late in the process. Or there is some really simple way of doing this but I missed it. Or maybe I should "apply" some BindComposer, but I am not sure how to do that.
Any helpful idea would be great!
Just to make sure I've understood:
You have some zul fragment (such as a reusable UI structure)
This fragment uses the MVVM pattern, but you want to choose a viewModel object when you are instantiating that fragment, instead of declaring a class name in the zul viewModel="#id()#init()" attribute
You want to initialize that fragment from a java class that has access to the UI (using Execution#createComponents to load the fragment and attach them to the page)
Does that sound correct?
If that's the case, you can make this way simpler
The attribute can be written as viewModel="#id('yourVmId')#init(aReferenceToAnAlreadyInstantiatedObject)".
Important note here: Notice that I have NOT put quotes around the object in the #init declaration. I'm passing an actual object, not a string containing a reference to a class to be instantiated.
When you invoke execution.createComponents() you may pass a map<String, Object> of arguments to the created page. You can then use the name of the relevant passed object when you create bind the VM.
have a look at this fiddle (bit rough, but it should make sense): https://zkfiddle.org/sample/2jij246/4-Passing-an-object-through-createComponents-as-VM#source-2
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("passedViewModel", new GenericVmClass("some value in the passed VM here"));
Executions.createComponents("./fragment.zul", e.getTarget().getPage(),null, args);
FYI if you are using ZK shadow-elements, you can also pass that object to the fragment from an apply with a source in pure MVVM pattern.
The <apply> shadow element for example can pass objects to the created content with a variable name, and you can use that variable name when initializing the VM.
Regarding BindComposer:
You need to instantiate BindComposer up to ZK 7.X
In ZK 8.X and above, BindComposer will be instantiated automatically when you use the viewModel="..." attribute on a ZK component.

yii2 ActiveForm without begin form tag

I need to generate widgets\ActiveForm::field() without "form" tag at begin. I looked in source of yii\widgets\ActiveForm and found that this can not be avoided :(
public function init()
{
if (!isset($this->options['id'])) {
$this->options['id'] = $this->getId();
}
echo Html::beginForm($this->action, $this->method, $this->options);
}
Maybe there's another way to solve this problem without extending the 'ActiveForm' class?
ActiveField is basically just a wrapper for yii\helpers\Html::active... methods so you just can call echo yii\helpers\Html::activeTextInput($model, $attribute, $options); without using ActiveForm

Call Helpers From Zend_Form

I try this codes, but not works:
$this->getView()->translate("Name"); //not work
$this->_view->translate("Name"); //not work
$this->view->translate("Name"); //not work
First of all, Zend_View is not injected into Zend_Form. So when you call $this->view or $this->_view it wont work, because there is nothing to return. Why getHelper() works? Because it fetches view via helper broker (and if your are using viewRenderer). Look below at the code:
// Zend/Form.php
public function getView()
{
if (null === $this->_view) {
require_once 'Zend/Controller/Action/HelperBroker.php';
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$this->setView($viewRenderer->view);
}
return $this->_view;
}
This is reason why $this->_view->translate() works if you call getView() before, because it's stored as protected property.
According to this, that code should work perfectly and works for me:
class My_Form extends Zend_Form
{
public function init()
{
echo $this->getView()->translate('name'); //fires 'translate' view helper and translating value
//below will also work, because you have view now in _view: getView() fetched it.
echo $this->_view->translate("another thing");
}
}
BTW. If your using translate helper to translate labels or names of fields, you don't have to. Will be enough, if you set translator object as a static property of Zend_Form, best in your bootstrap:
Zend_Form::setDefaultTranslator($translator);
And from that moment all fields names and labels will be translated automatically.
I don't no why, but when I add this function to my form, it work:
public function init() {
$this->getView();
}
this line works:
$this->_view->translate("Name");
View is not injected into Zend_Form (don't ask me why, when it's required for rendering). You have to extend Zend_Form and inject view inside yourself. Other option is using FrontController->getInstance() > getStaticHelper > viewRenderer and recieve view from it.

Zend Framework: Get whole out output in postDispath while using Layout

I have a layout loader plugin which looks like this:
class Controller_Action_Helper_LayoutLoader extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
$config = Zend_Registry::get("config");
$module = $this->getRequest()->getModuleName();
if (isset($config->$module->resources->layout->layout) && !$this->getRequest()->format)
{
$layoutScript = $config->$module->resources->layout->layout;
$this->getActionController()->getHelper('layout')->setLayout($layoutScript);
}
}
}
In a controller plugin I then want to get the whole of the response like so
$this->getResponse()->getBody()
This however only returns the output from the action, not the output from the layout too.
How can I get the whole output, layout and action together?
Thanks!
I believe that Zend_Layout operates at postDispatch() with a high stack index. So, to get the content, you might need to do your access later, at dispatchLoopShutdown().

ZEND Controllers -- How to call an action from a different controller

I want to display a page that has 2 forms. The top form is unique to this page, but the bottom form can already be rendered from a different controller. I'm using the following code to call the action of the other form but keep getting this error:
"Message: id is not specified"
#0 .../library/Zend/Controller/Router/Rewrite.php(441): Zend_Controller_Router_Route->assemble(Array, true, true)
My code:
First controller:
abc_Controller
public function someAction()
{
$this->_helper->actionStack('other','xyz');
}
Second controller:
xyz_Controller
public function otherAction()
{
// code
}
Desired results:
When calling /abc/some, i want to render the "some" content along with the xyz/other content. I think I followed the doc correctly (http://framework.zend.com/manual/en/zend.controller.actionhelpers.html) but can't find any help on why that error occurs. When I trace the code (using XDebug), the xyz/other action completes ok but when the abc/some action reaches the end, the error is thrown somewhere during the dispatch or the routing.
Any help is greatly appreciated.
You can accomplish this in your phtml for your someAction. So in some.phtml put <?php echo $this->action('other','xyz');?> this will render the form found in the otherAction of XyzController
The urge to do something like this is an indication you're going about it in totally the wrong way. If you have the urge to re-use content, it should likely belong in the model. If it is truly controller code it should be encapsulated by an action controller plugin
In phtml file u can use the $this->action() ; to render the page and that response would be added to current response ..
The syntax for action is as follows::
public function action($action, $controller, $module = null, array $params = array())
You can create new object with second controller and call its method (but it`s not the best way).
You can extend your first controller with the second one and call $this->methodFromSecond(); - it will render second form too with its template.
BTW - what type of code you want to execute in both controllers ?
Just an update. The error had absolutely nothing to do with how the action was being called from the second controller. It turns out that in the layout of the second controller, there was a separate phtml call that was throwing the error (layout/abc.phtml):
<?php echo $this->render('userNavigation.phtml') ?>
line of error:
echo $this->navigation()->menu()->renderMenu(...)
I'll be debugging this separately as not to muddy this thread.
Thanks to Akeem and hsz for the prompt response. I learned from your responses.
To summarize, there were 3 different ways to call an action from an external controller:
Instantiate the second controller from the first controller and call the action.
Use $this->_helper->actionStack
In the phtml of the first controller, action('other','xyz');?> (as Akeem pointed out above)
Hope this helps other Zend noobs out there.
Hm I can't find and idea why you need to use diffrent Controlers for one view. Better practise is to have all in one Controller. I using this like in this example
DemoController extends My_Controller_Action() {
....
public function indexAction() {
$this->view->oForm = new Form_Registration();
}
}
My_Controller_Action extends Zend_Controller_Action() {
public function init() {
parent::init();
$this->setGeneralStuf();
}
public function setGeneralStuf() {
$this->view->oLoginForm = new Form_Login();
}
}
This kind of route definition:
routes.abc.route = "abc/buy/:id/*"
routes.abc.defaults.controller = "deal"
routes.abc.defaults.action = "buy"
routes.abc.reqs.id = "\d+"
requires a parameter in order to function. You can do this with actionStack but you can also specify a default id in case that none is provided:
$this->_helper->actionStack('Action',
'Controller',
'Route',
array('param' => 'value')
);
routes.abc.defaults.id = "1"
For Me this worked like a charm
class abcController extends Zend_Controller_Action
{
public function dashBoardAction()
{
$this->_helper->actionStack('list-User-Data', 'xyz');
}
}
class XyzController extends Zend_Controller_Action {
public function listUserDataAction()
{
$data = array('red','green','blue','yellow');
return $data;
}
}