zend framework display/process form using same action/view? - model-view-controller

Using MVC architecture (Zend Framework), should you use a single view with if statements to display/process a form, or multiple views (i.e. one view to display form, one to display result).
In this instance, I am trying to produce a google like search engine. The layout of the page will fundamentally change when displaying search results.
For example; Controller:
public function indexAction()
{
if (!$this->getRequest()->isPost()) {
// display form
} else {
if ($this->_request->isPost()) {
if (!$form->isValid($formData)) {
// re-display form with errors
} else {
// process form and;
// display result using same action/view?
// display result using same action but use a different view?
}
}
}
}
Using the same view:
<?php if(isset($this->form)) : ?>
<!-- show form -->
<?php else: ?>
<!-- show result -->
<?php endif; ?>
Hope that makes sense.

Don't get caught up in the old monolithic style of coding where one hunk of code does everything for one output page. The whole point of MVC is so separate responsibility. Think of a view as a template for a single piece of output, regardless of what page/url it appears on. The form is one piece of output. The search results is another. Use two views.

Less if-s -- less bugs. I propose you to use more different views here, that can reuse one form.

Related

Prestashop: how to display messages in the cart

i'm working in a quota module for PrestaShop 1.6 and in one of the features is that a need to display a message in the cart's page and in that modal that opens when you add an item to the cart from the home page.
I'm working with the hookCart the hook function, public function hookCart($params), is in my module's class. From it I can get the instance of the Controller like this $this->context->controller
My question is how to display messages in these two views? I tried adding them to the errors array. I can see the message but not the way I'm supposed to display. I would like to display in those alert classes from bootstrap.
The views:
Can you help me?
To display a message on the checkout page you can use whatever front controller hook you want. I guess the one that makes most sense would be displayTop. We won't be outputting any html to the top but just add a message to the controller's errors array.
public function hookDisplayTop()
{
$controller = $this->context->controller;
if ($controller->php_self != 'order' && $controller->php_self != 'order-opc') {
return false;
}
/*
You can do custom logic here if you want to display message only
on some conditions or only on specific step of the checkout
*/
$controller->errors[] = $this->l('Some message');
return false;
}
For the popup the things get messy because:
Popup doesn't have an error display area
Blockcart AJAX uses weird logic that calls CartController which includes blockcart-ajax.php which loads blockcart module method which loads a JSON template file to output data (???)
One way to do this is to use the hook actionCartSave. This hook gets executed on pretty much all cart operations, so we need to make sure we are adding our message when a product is added to cart, uses ajax etc.
public function hookActionCartSave()
{
// If cart doesn't exist or product is not being added to cart in ajax mode - do nothing
if (!$this->context->cart || !Tools::getValue('id_product) || !Tools::getValue('add') || !Tools::getValue('ajax')) {
return false;
}
/*
You can do custom logic here if you want to display message only
on some conditions
*/
$this->context->smarty->assign('mycartpopupmessage', $this->l('Message');
return false;
}
Then you will have to modify themes/default-bootstrap/modules/blockcart/blockcart-json.tpl file to add your message into JSON template.
...
"wrappingCost": {$wrapping_cost|json_encode},
"nbTotalProducts": {$nb_total_products|intval},
"total": {$total|json_encode},
{if isset($mycartpopupmessage)}
"mycartpopupmessage": {$mycartpopupmessage|json_encode},
{/if}
....
Then you need to modify themes/default-bootstrap/js/modules/blockcart/ajax-cart.js and add the following
if (jsonData.mycartpopupmessage) {
$('#layer_cart .alert-message').html(jsonData.mycartpopupmessage);
$('#layer_cart .alert').removeClass('hidden');
}
else {
$('#layer_cart .alert').addClass('hidden');
}
And at last modify themes/default-bootstrap/modules/blockcart/blockcart.tpl
and add alert div
<div class="alert alert-danger hidden">
<button data-dismiss="alert" type="button" class="close">×</button>
<p>{l s='There is 1 error' mod='mymodule'}</p>
<ol>
<li class="alert-message"></li>
</ol>
</div>
Now you should be getting a bootstrap alert inside a popup.
Quite some native prestashop modules haven't been (I guess) updated in years as a lot of them are good candidates for an extensive rework or at least compliant with the MVC workflow which would make modifications like yours much more simple.

How do I auto fill field values in a section of a form that is loaded via ajax in Laravel 4?

I have a section of a form that dynamically loads different sets of fields based on the user's selection in a control. I'm using a javascript event handler to detect when the selection changes, and using AJAX (with HTML payload) to pull in the proper set of fields.
I would like to be able to use Laravel's Form::getValueAttribute() method to automatically fill in the form fields' values in both the static and dynamic form parts. However, the partial view that is loaded by my AJAX call does not have the same instance of the Form class as the view with my main Form, so I can't simply call getValueAttribute() in the partial.
My thought is to make the AJAX call a POST, and serialize the necessary data (a subset of Input::old() or the model data depending whether the page is loaded as the result of validation errors, or an UPDATE request) to send along with the POST so that the HTML fragment I get back has the values set properly.
Is this the best way to get what I want? If so, does Laravel have any tools to help with the serialization of form data? If not, what might be a better approach?
I've found an approach I like better. When the view is loaded normally I use AJAX as usual to load the partial. But when the view is loaded for a validation post-back or for editing, I use Laravel's Views' nest method to nest the partial view containing the proper fields directly into the response. The partial then has access to all the Input and error data I need. The user is still able to change the field set as usual but I put up a confirm prompt for them if they have already set some values in a field set they previously selected. If they decide to proceed anyway, the field set is cleared and a new field set is brought in via AJAX as usual.
My code looks something like this:
Controller:
public function newThing() {
if ( Request::session()->has('errors') ) {
// this is a validation post-back
return View::make('thing')
->nest('fields', 'fields_partial');
} else {
// just a normal unfilled form
return View::make('thing');
}
}
public function editThing() {
return View::make('thing')
->nest('fields', 'fields_partial');
}
View: thing.blade.php (just a snip of it)
...
<form>
...
<select id="picker">...</select>
<div class="sub-fields">
{{ isset($fields) ? $fields : '' }}
</div>
...
</form>
...
<script>
$('#picker').change(function() {
// if any .sub-fields inputs have been changed, get confirmation from the user
// if user confirms, do ajax stuff to replace .sub-fields contents with new field set
// otherwise cancel the change
});
</script>

Rendering part of a view from another view

I'm moving my WebForms project to MVC and having a hard time designing things.
The basic display of my app is in _Layout. The page is divided into 4 parts(say Part A,B,C and D), with 3(A,B,C) just containing html and one(D) is dynamic. I had used #RenderBody to bring in the content of Part D. However, now the other parts are changing and I need separate controllers for these parts. What is the best way to get their contents to be displayed into _Layout?
#Html.RenderPartial / #Html.Partial / #Html.RenderAction / #Html.Action ?
I'm currently trying to replace Part C by using -
#Html.Action("Index", "CController")
However, this is not working.
In Index.cshtml for CController, I've the Layout = null, initially it was set to point to _Layout.cshtml, but I read here that this created issues.
After putting the C Part in CControllers view, it does not event display the basic _Layout page that it displayed earlier.
Here's the Index.cshtml of CController -
<div id="noteContainerDiv">
Here goes all the data to display
</div>
And Here's the code for CController.cs -
public class CController : Controller
{
public ActionResult Index()
{
return PartialView();
}
}
Can anyone suggest the right way to design this?
What you could do is have a view model for your subview (the one you call D) and pass in this view model from your layout view.
I.e. by doing something like this in your _Layout:
#Html.Partial("_SubviewD", Model.SubviewDModel)
Then obviously in your controllers you need to initialize this subview model and include it in your model (or alternatively, in the ViewBag--apologies for suggested being evil!) You could even find ways to do this without changing all your controllers (for example through a base Controller class and overriding OnActionExecuted).
There are many ways you could do this. But I would stick to your initial approach.
Have your CController Index action return a PartialViewResult instead of a full result.
You don't set the layout for partial views. So in your CController index action you'll have something like this:
var model = ....
return this.PartialView("NameOfYourPartialView", yourModel);
And when you call
#Html.Action("Index", "CController")
Everything should be OK, cool?

How would I structure this in Zend Framework?

I'm coming from CodeIgniter, and the terminology overlap between it and other MVC frameworks (particularly Zend) is giving me a mental block of some kind.
I want my users to visit http://mysite.com/do/this.
I understand that "do" is the Controller, and "this" is a function (well, method) within that Controller.
My site will have common elements like a header and sidebar; I understand that those go into a Layout, which will be incorporated into the final output.
I want the /do/this page to display three visual blocks of information (I'm deliberately not using the word "modules"). Let's call them BlockA, BlockB, and BlockC. Maybe one is a list of "new events" and another is a list of "new posts" and another is something else. Whatever. The trick is, these blocks of information will also be displayed on other pages of the site - say, http://mysite.com/did/that.
Both the "did" and the "do" Controllers (and the "this" and "that" methods, obviously) would be arranging BlockA, BlockB, and BlockC differently. Each Controller would have different criteria for what went into those blocks, too - one might be current information, while another might be archived information from the past.
I want to ensure that future programmers can easily alter the appearance of BlockA, BlockB, and/or BlockC without having to touch the code that populates their data, or the code which arranges them on each page.
So my general feeling is that BlockA, BlockB, and BlockC need to have their visual layout defined in a View - but that View wouldn't be specifically associated with either the "do" or the "did" Controllers. And the code which populates those blocks - that is, queries information from a database, selects the bits that are to be displayed, and whatnot - shouldn't reside entirely in those Controllers, either.
I started down the path of putting the logic - that is, assembling what will be displayed in each block - into Models. I feel I'm on the right path, there; both the "do" and "did" Controllers can thus summon the block-creation code via Models. But how (and where) do I abstract the visual element of those blocks, in such a way that the visual elements can also be shared by these two Controllers? Do the Models somehow load a View and output HTML to the Controllers (that doesn't feel right)? Or is there a way for the Controllers to run the Model, get the data to display, and then somehow feed it to a common/centralized View?
I know how I'd do this in CodeIgniter. But... what's the correct architecture for this, using Zend Framework? I'm convinced that it's very different than what CodeIgniter would do, and I want to start writing this application with the right architecture in mind.
One small naming thing: /:controller/:action/* => /do/this = this is an action (although also both a function and a method in the controller, action is the proper name)
Your blocks to me sound like "partial views". There are a few ways to approach this problem, and depending on how the views work, or what information they need to know, you adapt your strategy
Rendering Partials
You want to use this method when you have some view code you want to be used by multiple views. There are two different approaches using the view helpers Zend_View_Helper::render or Zend_View_Helper_Partial* The render($phtmlfile) view helper is more efficient, the partial($phtmlfile, $module, $params) view helper clones a new view, unseting all parameters, and setting the ones you pass in. An example of how to use them:
case/list.phtml:
<?php
$this->headTitle($this->title);
// works because our controller set our "cases" property in the view, render
// keeps our variables
echo $this->render("case/_caseListTable.phtml");
case/view.phtml
<?php
$this->headTitle($case->title);
?><!--- some view code showing the case -->
<?php if ($cases = $case->getChildren()): ?>
<h3>Children</h3>
<?php echo $this->partial("case/_caseListTable.phtml", "default", array(
"cases"=>$cases,
)); ?>
<?php endif; ?>
case/_caseListTable.phtml
// table header stuff
<?php foreach ($this->cases as $case): ?>
// table rows
<?php endforeach; ?>
// table footer stuff
Custom View Helpers
Sometimes the controller has no business knowing what information is being displayed in the block, and preparing it for your view would be silly, at this point you want to make your own view helpers. You can easily add them to the global view in application.ini:
resources.view.doctype = "XHTML1_STRICT"
resources.view.helperPath.My_View_Helper = APPLICATION_PATH "/../library/My/View/Helper"
I have a tendency to use this method for things that will require additional information from the model not provided by the controller, or blocks of reusable formatting code for the view. A quick example, from a project that I used: Olympic_View_Helper_Ontap grabs the draught beer list and renders it:
class Olympic_View_Helper_Ontap extends Zend_View_Helper_Abstract {
public function Ontap()
{
$view = $this->view;
$box = Olympic_Db::getInstance()->getTable('box')->getBoxFromName('Draught-Beer');
if ($box) $menu = $box->getMenu(); else $menu = null;
$content = "";
if ($menu)
{
$content = "<h1>".$view->escape($menu->title)."</h1>";
$content .= "<ul>";
foreach($menu->getItems() as $item) {
$content .= "<li>".$view->escape($item->name)."</li>";
}
$content .= "</ul>";
}
return $content;
}
}
Then in my layout:
<?php echo $this->ontap(); ?>
Your View Helpers can also accept arguments (of course), can call other view helpers (including partial). Consider them template functions. I like using them for short tasks that are required a lot, for instance $this->caseLink($case) generates a properly formatted <a href='/case/2' class='case project'>Project</a> tag.

How do you build a Single Page Interface in ASP.NET MVC?

I want to build a webapplication with a "Single Page Interface", using ASP.NET MVC.
I have searched if this was at least possible and I think the answer is: not by simple means (reading http://msdn.microsoft.com/en-us/magazine/cc507641.aspx#S2 second-last paragraph; that article is from May 2008, though).
I found other examples which implemented this by coding/hacking with jQuery. However, I'm looking for a clean solution, using standard .NET approaches, if possible.
What I want is precisely the same functionality when you create a new "MVC Web Application". However, instead of links to "/Home/About" which reloads the entire page, I want links to "#Home/About" which loads only the new part via AJAX.
The standard approach of calling templates (partial views) with Html.RenderPartial is exactly what I want, only then loading them in through AJAX-requests.
Of course, it might be that I can't use these templates that are rendered by the Master-page for some reason (maybe it's expecting to always be called in a certain context from a certain place or so). But maybe there's another clean solution for how to build your template-pages and fetching them from the Master-page.
Who has a nice solution for implementing such thing, a Single Page Interface?
PS: I'm developing in Visual Web Developer 2008 Express Edition with MVC 1.0 installed, in C#
[edit]
Below I read that working with the templates is possible and that jQuery looks indeed like inevitable, so I tested it.
The following code transforms regular links created by Html.ActionLink into anchor-links (with #) to contain history, and then fetch the page via AJAX and only injecting the html-part I'm interested in (i.e. the partial page inside div#partialView):
$("a").each(function() {
$(this).click(function() {
$("div#partialView").load($(this).attr("href") + " div#partialView");
location.hash = $(this).attr("href");
return false;
});
});
These links also allow for graceful degredation.
But what I have left now, is still fetching the whole page instead of only the partial page. Altering the controller didn't help; it still provided me html of the whole page, with all of these statements:
public ActionResult About()
{
return View();
return View("About");
return PartialView();
return PartialView("About");
}
How could I only return the content of the part I'm interested in (i.e. the contents of Home/About.aspx)?
What I'd like is POSTing a value with AJAX (e.g. "requesttype=ajax") so that my controller knows the page is fetched via AJAX and only returns the partial page; otherwise it will return the whole page (i.e. when you visit /Home/About instead of #Home/About).
Is a good practice to alter Global.asax.cs maybe, to create a new routing schema for AJAX-calls, which will only return partial pages? (I haven't looked into this much, yet.)
[edit2]
Robert Koritnik was right: I also needed an About.ascx page (UserControl) with only the small HTML-content of that page. The first line of About.aspx was linked with the Master-page via MasterPageFile="~/..../Site.master" which caused that all HTML was printed.
But to be able to execute the following in my controller:
public ActionResult About()
{
return Request.IsAjaxRequest() ? (ActionResult)PartialView() : View();
}
I needed to alter the way a PartialView (.ascx file) and a View (.aspx) file was found, otherwise both methods would return the same page (About.aspx, ultimately resulting in an infinite loop).
After putting the following in Global.asax.cs, the correct pages will be returned with PartialView() and View():
protected void Application_Start()
{
foreach (WebFormViewEngine engine in ViewEngines.Engines.Where(c => c is WebFormViewEngine))
{
/* Normal search order:
new string[] { "~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx"
"~/Views/Shared/{0}.ascx"
};
*/
// PartialViews match with .ascx files
engine.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.ascx" };
// Views match with .aspx files
engine.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
}
RegisterRoutes(RouteTable.Routes);
}
Full view vs. Partial view
Seems like you've messed up something. If you create an About.aspx page with all the HTML needed to display the whole page it doesn't really matter if you say
return PartialView('About');
The view still returns all the HTML that's written in it.
You should create a separate About.ascx that will only have the content of the page itself without the header and other stuff that's part of the whole page.
Your original page About.aspx will have something like this in its content (to avoid repeating writing the same content twice):
<%= Html.RenderPartial("About") %>
And you can have two controller actions. One that returns a regular view and one that returns a partial view:
return View("About"); // returns About.aspx that holds the content of the About.ascx as well
return PartialView("About"); // only returns the partial About.ascx
Regarding routes in Global.asax
Instead of writing separate routes for Ajax calls you'd rather write an action filter that will work similar as AcceptVerbsAttribute action filter. This way your requests from the client would stay the same (and thus preventing the user from manually requesting wrong stuff), but depending on the request type the correct controller action will get executed.
Well, you can load Partial View through AJAX request. In example, I'll use jquery to make an ajax call.
These could be the action in controller (named HomeController):
public ActionResult About()
{
//Do some logic...
//AboutView is the name of your partial view
return View("AboutView");
}
JQuery ajax call to place the retured html in place you want:
var resultDiv = $('#contentDIV');
$.ajax({
type: "POST",
url: "/Home/About",
success: function(responseHTML) {
resultDiv.replaceWith(responseHTML);
}
});
[edit-question is updated]
It is possible to do exactly what you want. First controller action can give you back the partial view, so mine "AboutView" could have been something like this:
<table>
<tr>
<th>
Column1Header
</th>
<th>
Column2Header
</th>
</tr>
<tr>
<td>
...
</td>
<td>
...
</td>
</tr>
and this HTML is exactly what are you going to have in responseHTML on success handler in jquery ajax method.
Second, you can distinguish in controller action if the request is an ajax request:
public ActionResult About()
{
//partial AboutView is returned if request is ajax
if (Request.IsAjaxRequest())
return View("AboutView");
else //else it will be the default view (page) for this action: "About"
return View();
}
We've got a site that does exactly this, and you really want to use the jQuery route here--alot easier to implement in the long run. And you can easily make it gracefully degrade for users who don't have javascript enabled--like google.
it isn't all that clear what are you asking for, is it for a complete example or for some specific functionality? You should be able to do this without JQuery for simple scenarios, you can use the Ajax view helpers like the ActionLink method. Also, I don't really understand what is your issue with RenderPartial, but maybe you're looking for something like RenderAction from ASP.NET MVC Futures.
ASP.NET MVC 4 (now in beta) adds support for Single Page Applications in MVC.
http://www.asp.net/single-page-application
UPDATE:
...and they removed it from the MVC 4 RC
UPDATE:
...and it is back with the 2012 Fall update

Resources