views:

315

answers:

8

I'm still somewhat confused by how MVC is supposed to work.

Lets say I have a website selling widgets. I have a listing page, /widgets/list and a product page /widgets/product/123.

Both of these can use the widget controller and call the list and product methods - simple enough so far. Lets say I also have several other controllers for various things.

Now I add a newsletter signup box into my header - i.e. on every page of the site. How is this going to work? I get the idea that it should submit to /newsletter/signup

But what happens if there's an error (say you didn't fill in your email address correctly)? It should show whatever page you were on (e.g. /widgets/list) but the newsletter controller needs to run. The widget controller doesn't know about the newsletter controller so I can't put the code there... How is this supposed to work?

Edit: No AJAX please - I can understand that more easily. Consider this the fallback when javascript is disabled.

Edit 2: Any examples or tutorials covering this kind of thing would be much appreciated

Edit 3: Is it allowable for a view to call an action? For example the header might call Newsletter->index()

A: 

You must put the error message in some global place where the page controller (which includes the "subcontrollers" newsletter) can pick it up.

In case of AJAX, you can have the newsletter controller talk to the DIV in which is it visible (since you're not reloading the whole page). For that, you'll need a piece of JavaScript in the page that is called when the AJAX request finishes which takes the string and puts it where ever you want it.

Aaron Digulla
A: 

My MVC experience is more practical and less "what the book says", but here goes:

You'd have a base controller class (in CakePHP - which is what I'm most familiar with - it's called AppController) which is subclassed by all other controllers. This class would implement all of the "global" stuff you're talking about.

Practical example:

In my AppController class, the framework defines a beforeFilter() callback which is executed essentially on each page load. This is where I'd check to see if the signup form had been submitted and handle it appropriately. If the signup took a crap somehow, I'd add something to the session indicating as much and my view would simply check for the existence of a list of errors originating from the newsletter model and, if they're there, show them.

This is probably a little heavier on nuts and bolts and lighter on theory than you were asking for, but I have no formal training in any of this crap so there's my best :)

inkedmn
+1  A: 

Add a field to the newsletter form, that stores the URL of the current page. When an error occurs during submission of the newsletter, retrieve the URL and redirect to that page. Providing that you put the error information in the correct place, it should be picked up by the newsletter form which, you say, is included in every page.

belugabob
A: 

For widgets that are supposed to return some validation errors etc. - use partial requests (or subcontrollers from MvcContrib) + AJAX.

Arnis L.
A: 

What I do is have each form post to itself. In the controller, I check to see if the post variable is set; if so, I do validation. If the validation succeeds, I do a redirect to another page. If it fails, the form page just gets reloaded with error messages. This makes it easy and reduces code duplication. See here:

**in controller**:

If post variable is set:
    validate form

    if form is validated:
        redirect to new page (or whatever)
    else:
        add error messages to the $data variable of the view
    endif

endif

//$data contains whatever information you'd normally pass to the view.
//if there was a validation error, the messages are added to the $data variable
show view with $data variable

As you can see, the flow always falls through to the single view-loading statement. However, if validation succeeds, you are whisked away to another page.

ryeguy
If I have an existing website with 10 controllers, each with 5 actions, doesn't that mean I'd have to edit 50 functions if I added a newsletter signup into the header?
Greg
Well that would be the exception where it posts to a separate page. Most forms don't appear on multiple pages, only stuff like newsletters, login, and maybe registration. On the whole, the above is pretty effective.
ryeguy
+5  A: 

I don't see why the error message for the newsletter box thats on every page, has to be rendered in the same page. If you have a page which posts to another action - completely unrelated to the current view (for example - search) then there is no reason for the error message to be shown in the original page. Would you show the success message on the same page? Where would that be handled?

Error messages for the newsletter form should be shown in a view dedicated to the newsletter. For example, see how its done in Stackoverflow - go into the search box and type nothing, simply hit enter. This is a kind of error since you did not specify what you want to search for. Stackoverflow will then take you to a different page that explains how search work.

Now why did it do that? The reason is simple - the user was on some page and chosen to engage in an activity that is not related to the current page, so there is no reason to keep them there.

Guss
Although I can imagine the OP wants to return back to the originating view, I would probably choose your suggested approach also.
fireeyedboy
To add to that: To keep a "marker" of where the user last was, you can either use cookies or append a query string to the URL. For example: `/newsletter/signup?return_to=widgets/list`
musicfreak
+1 In my point of view it´s completely fine and acceptable to redirect user to another controller to inform of success/failure.
Luiz Damim
A: 

How about adding a hidden field to the page submitting to the /newsletter/signup controller with the url of where to go after the controller is finished i.e. the current page (or you could use the referer http header).

This controller then adds a list of error messages or a success message to a list of objects to be rendered by the view before forwarding on to the controller specified by the hidden field above. This controller then adds it's list of objects to be displayed in the view (eg. list of widgets).

Then in the view, you can display the error messages from the newsletter controller if present.

Neal Donnan
+1  A: 

There is a good ASP.net MVC centric tutorial here http://bit.ly/Yw8CT describing methods to include widgets (reusable components) in an MVC environment.

The basic idea is to set up widgets with their own request pipeline - not amalgamate them into a combined controller/view which would defeat the maintainability of MVC.