I have two actions for image albums in my controller, the first creates a form to create a new album and handles the POST and the second generates a list of the existing albums. The form posts to the first action and then puts the list action on the actionstack. This order is necessary as otherwise a newly created album wouldn't appear in the list.
So in my HTML output there is first the output of the createAction-view and then of the listAction-view. But for styling reasons I want to change the order of view output of the two actions in my HTML. Is there a way I can have the list followed by the form without changing the order in which the actions are executed?
You should try and avoid using the action stack if at all possible. You can avoid this requirement completely by simply redirecting to the list view after handling the POST data. This also prevents users accidentally submitting the same album again by refreshing the page. If you still want the form to be displayed under the list, just add the form to that action as well so you can output it in the view under your albumn list.
public function createAction()
{
$form = new Yourapp_Form_Album();
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost()) {
// save the data and redirect
}
}
$this->view->form = $form;
}
public function listAction()
{
// load albums and assign to the view
// if you want the form under the list
$this->view->form = new Yourapp_Form_Album();
}
Related
I have a need for chained select boxes in my zend framework project: Countries->Regions->Provinces->Towns.
I am using zend form and intend to resubmit to reload the contents of the of the chained select boxes when one of them is changed. I have written some code in a PHPunit test to simulate what I will need in my controllers.
I will require to use this regional structure in quite a few different forms on my site and also plan to enhance with AJAX.
I don't want to be duplicating this code, so where should I store it and how should it be structured so that I can reuse its functionality. I thought perhaps an action helper?
public function testCanGetRegionalStructureFromUser() {
$user = new \Entities\User;
$user = $this->em->getRepository('Entities\User')->findOneByEmail('testuser#yahoo.com');
$town = new \Entities\Town;
$town = $user->getTowns_id();
// if user has not town input, then use the default
if (is_null($town)) {
$config = Zend_Registry::get('config');
$defaulttownid = $config->towns->defaultid;
$town = $this->em->getRepository('Entities\Town')->find($defaulttownid);
}
// get the town id
$townid = $town->getId();
//get the province
$province = $town->getProvinces_id();
$provinceid = $province->getId();
//get the region
$region = $province->getRegions_id();
$regionid = $region->getId();
//get the country
$country = $region->getCountries_id();
$countryid = $country->getId();
$countrylist = $this->em->getRepository('Entities\country')->findActiveCountries();
$regionlist = $this->em->getRepository('Entities\Region')->findActiveRegions($countryid);
$provincelist = $this->em->getRepository('Entities\Province')->findActiveProvinces($regionid);
$townlist = $this->em->getRepository('Entities\Town')->findActiveTowns($provinceid);
}
countrylist,regionlist etc. are ready to be injected into my form as options that will be used to populate the select boxes.
I think that for this case, creating a composite Zend_Form_Element makes the most sense.
This way, you can easily add the element to your forms with only a few lines of code, you can have validators built into the element so you aren't repeating that logic, and with your own decorator you can easily control the layout of the selections and only have to change one file to reflect the changes to all forms.
For one of my projects I created a composite element that had a plain text box that used autocomplete to search customers in real time as the user types. Once a customer is selected from the autocomplete list, an ajax call is made that fetches a list of properties owned by that customer and a dropdown box is updated with the new list.
The element provides access to the data values (customer, property) and the decorator renders the individual elements in a group, and set up the necessary event handlers and ajax calls (most of that code is in an external .js file.
Creating and Rendering Composite Elements
Similar to the above reference, by Matthew Weier O'Phinney
Video: Creating composite elements
I would like to extend the Contact Detail View so that a Detail View of the associated account appears on the same view.
MY instinct is to override the display function for the Contacts Detail View and from there create an instance of the Accounts Detail and attach it's display output.
But I don't know if there is a standard way of pulling this of.
I learned that in the up coming version (6.3), there will be a way of generating computed fields that have access to the fields of a related module.
If this is the case, then one option will be to create computed fields that reference the Account fields and then add a panel to Contact DetailView with the referenced Account fields.
Though, my original hunch proved to be doable as well and not as hacky as I had assumed at first:
<?php
require_once('include/MVC/View/views/view.detail.php');
class ContactsViewDetail extends ViewDetail {
function ContactsViewDetail() {
parent::ViewDetail();
}
function preDisplay(){
parent::preDisplay();
// Configuration to display All account info
$this->dv2 = new DetailView2();
$this->dv2->ss =& $this->dv->ss;
$this->bean2 = new Account();
$this->bean2->retrieve($this->bean->account_id);
$accountMetadataFile = 'custom/modules/Accounts/metadata/detailviewdefs.php';
$accountTemplate = 'custom/modules/Accounts/tpls/AccountsDetailView.tpl';
$this->dv2->setup('Accounts', $this->bean2, $accountMetadataFile, $accountTemplate);
}
function display(){
parent::display();
// Display Accounts information.
$this->dv2->process();
echo $this->dv2->display();
}
}
?>
In summary
Override the detail view.
Add a new display to the current View.
Add a new bean (module) to the View.
Process the display with the new bean.
Echo the display.
Another easier option may be just add an iframe field, which loads the detailview on the account inside of it. Not as pretty, but lots less hacking as well.
Is there a way to repopulate a form's file inputs in an Edit action's view? I'm using the same editor template for both my Create and Edit actions, and would like it so that when the form comes up during editing, the file input fields are automatically repopulated from the view model.
If so, the file names would come from the back end (since the files associated with the model are already in the system). With that being the case, would they still be considered HttpPostedFileBase objects, even though they didn't originate from the user's machine?
when you have your edit action you should pass model to view again:
//Get
public ActionResult Edit(int id){
YourModelOrEntity model = load content here
return View(model);
}
//[get]
public ActionResutl Edit(MyModel model){
if(! ModelState.IsValid) return View(model); //<- this is how you pass it back to the user
}
This might sound like an odd scenario, but I've got two forms on one page. One is just posting back to itself. I made the second post to another action to keep the code cleaner. Maybe not the right choice...
The problem I'm having now is that if that second form doesn't validate, I redirect back to the page with the form but I don't know how to keep my form fields filled in with the original information the user entered. Is there a way to do that and keep posting to two separate actions, or do I need to just bite the bullet and have both forms post back to the same action and deal with the messy logic?
I would submit both forms to the same action. There really shouldn't be anything too messy about it. In each form include a hidden field to signify which form is being submitted.
Application_Form_Login:
/* other form elements */
$this->addElement('hidden', 'login', array(
'value' => 1
));
Application_Form_Register:
/* other form elements */
$this->addElement('hidden', 'register', array(
'value' => 1
));
Controller:
$loginForm = new Application_Form_Login();
$registerForm = new Application_Form_Register();
if($this->_request->isPost()) {
if($this->_request->getPost('login')) {
if($loginForm->isValid($this->_request->getPost())) {
// validated, redirect
$this->_helper->redirector('profile', 'user');
}
}
if($this->_request->getPost('register')) {
if($registerForm->isValid($this->_request->getPost())) {
// validated, proceed as needed
}
}
}
$this->view->loginForm = $loginForm;
$this->view->registerForm = $registerForm;
View:
echo $this->loginForm;
echo $this->registerForm;
With this type of a setup, if either of your forms fail validation, isValid() will preserve any data that has been entered and you still redirect on a successful validation of one or both of the forms.
Personally, I think that each form should post to its own controller, as you have. This keeps the code for processing that form in a single place. The issue here is that you want to return to the original page on failed validation. But why? Why not simply redisplay the form in the target controller, just like you would if there were a single form on the page?
For example, consider a login form that appears on every page of a sie (perhaps because it in the site template/layout). It posts to something like AuthController::loginAction(). If the login fails, then you don't typically send him back to the page from which he came. You leave him at the login page, with the form as pre-filled from the $_POST as you want it to be (probably a username, but not his password).
See this answer for a similar discussion.
Update: Had another thought in this. If you really want to handle the processing in two different controllers in order to keep him on the page from which he posted the form, at least extract that form processing out into an action helper. This way, you could at least keep that form-processing DRY.
Update: Rob Allen has just written a great blog post "A form in your layout" in which he describes a method that uses an action-helper with a preDispatch() method that instantiates and processes the form. Very nice.
How do you redirect? I don't see the problem if you just display the form page again. You can prefill you forms using Zend_Form::populate().
Well, I would just keep both forms submitting on the same page.
I don't see why your code should get any less readable. Learn how to use action helpers and your controllers will suddenly look extremely simple and readable:
public function indexAction()
{
$request = $this->getRequest();
// send forms to view, so we can print them
// but also so we can access them in action helpers
$this->view->form = $this->_getForm('Form1', '/');
$this->view->form2 = $this->_getForm('Form2', '/');
if ($request->isPost())
{
// process the first form
if (isset($_POST['form_submit_button']) && $this->view->form->isValid($_POST))
{
$this->_helper->form($this->view->form->getValues());
}
// process the second form
else if (isset($_POST['form2_submit_button']) && $this->view->form2->isValid($_POST))
{
$this->_helper->form2($this->view->form2->getValues());
}
}
Each form's processing would have its own action helper.
I have a form with a grid and I want to pass info from the first form to second based on which row is selected when the user clicks the edit button.
what is the best way? and how should I decide how the form should be blank if the the user wants to add a new or fill the second(edit) form with the values from the selected row of the first forms datagrid?
The row values are all properties of the same object.
I can delete and add a new object, its editing an exsisting one that I am having a hard time with, and how should I load the second form?
I am currently creating and instance then instance.Show();
This is working the open a blank form, but I want to loadd it with the object based on the selected row when the user wants to edit an exsisting record.
Let's say your form1 is the form with data grid (grdMyData) that's displaying rows of MyClass class instances, and form2 is the form for editing the data of the given row.
When user clicks Edit, you could use this:
private void btnEdit_Click(sender e, EventArgs arg)
{
if (grdMyData.SelectedRows.Count == 0)
return; //nothing to do
MyClass selectedRow = (MyClass)grdMyData.SelectedRows[0].DataBoundItem;
Form2 frm2 = new Form2(selectedRow);
if (frm2.ShowDialog() == DialogResult.OK)
{
//do something if needed
}
}
This code is assuming you have the proper Form2 constructor which takes the type of object it works with. With this when you are working in Form2 the data will automatically affect the Form1 display because they're working with the instance of the same object.
I would suggest exposing an event in one form that the other form can consume.
Here is the offical tutorial
http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx
Basically it would be something like this
// Source form
public event YourEventHandlerType EventName;
// Wherever the event occurs
EventName.Invoke(...);
// Destination form
this.referenceToSourceForm.EventName += MyEventHandler(...);
So, you will need some reference to the source form in the destination form, or you will need to setup the event handling outside of the two forms otherwise.