My Example
Relatively simple layout.xml.php:
<?xml version="1.0" encoding="<?php echo sfConfig::get('sf_charset', 'UTF-8') ?>"?>
<example>
<?php echo $sf_content ?>
</example>
Simply isn't being used by any XML templates e.g. indexSuccess.xml.php
The Symfony documentation states:
The layout is automatically disabled for XML HTTP requests and non-HTML content types, unless explicitly set for the view.
Yet gives no documentation on how to set explcitly? Elsewhere obviously leads to:
all:
layout: layout
has_layout: true
But this seems to make no difference for XML templates?
Other sources mention sfAction having a hasLayout method, which clearly has been deprecated.
Evidently seems this isn't something that can be set globally via YAML (which is sad).
You can set it as stated in the documentation per view, i.e. in view.yml:
indexSuccess:
layout: layout
has_layout
But this is pretty laborious if you have many actions and against DRY concepts.
Note: Setting the values for all takes no effect.
$this->setLayout('layout')
Does work within an action, but again in my scenario this would need to be set in every action, again not particularly DRY.
Thus, I chose to extend sfActions and bind it into the preExecute method.
class myActions extends sfActions {
public function execute($request) {
if($request->getRequestFormat() == 'xml') {
$this->setLayout('layout');
}
return parent::execute($request);
}
}
Sorts the problem globally if you make sure all your actions extend myActions instead of sfActions, if you want to do it for all formats you could make use of the preExecute method instead, but I wanted to make use of the sfWebRequest to ensure I don't try and force layouts onto prospective other formats I may add such as JSON.
Might this be part of "setting it explicitly for the view"?
$response = $this->getResponse();
$response->setContentType('text/xml');
http://www.symfony-project.org/gentle-introduction/1_4/en/07-Inside-the-View-Layer
Related
I have correctly set up a form in ZF2, which includes several elements and a collection containing a fieldset that can be duplicated by the user. All works fine, but I need more control on how the fieldset renders, ie. I need to add other things between some elements in the fieldset, rather than just output them one after another.
I'm not expert in ZF2, but from research I seem to understand that you can declare a custom fieldsetHelper in the formCollection, however:
I can't manage to do so; I tried to extend the FormCollection class, and adding protected $fieldsetHelper = 'myFieldsetHelper';, which is also declared in the module config, but I get:
Fatal error: Call to undefined function myFieldsetHelper()
I'm familiar with extending FormRow helper, but I don't know how the fieldset helper should be written (I only need to add my extra stuff between some elements), and can't find any example on the web.
Any help, please?
You can use other view helpers to customize the forms layout in your view script such as FormLabel, FormText, etc.
Please see my answer here
EDIT
You can also loop through all the collection elements in your view script and render separately.
<?php
foreach ($this->form->get('collection') as $fieldset) {
echo $this->formText($fieldset->get('elementName'));
}
?>
I created a project that the nodes are defined using attributes, and I set it in the web.config to scan for attributes, and it works fine.
I don't use an XML file at all.
Now I want to add a dynamic node provider, how do I do it?
Is there a way to do it without the XML (.sitemap) file?
I need to make sure it's under the root, which has been set in code using MvcSiteMapNodeAttribute attribute.
I've read the documentation and I can't really figure out where to place this line:
<mvcSiteMapNode
title="Details" action="Details"
dynamicNodeProvider="Project.StoreDetailsDynamicNodeProvider, Prject" />
What action is it supposed to point to? Additionally as said above, the root element is defined using attributes, so my question is if there is a way to avoid XML, or alternatively what's the efficient way to declare the XML (the less the better) to include my dynamic provider.
Update
I've tried the following and the node provider still isn't reached (From HomeController.cs).
[MvcSiteMapNode(Title = "Home", Key = HomeMenuKey,
DynamicNodeProvider = "Project.Namespace.NodeProvider, Assembly")]
public ActionResult Index()
{
return View();
}
Can you define it in the controller method attributes (and not use XML at all)?
For example:
[MvcSiteMapNode(Title="Details",
DynamicNodeProvider = "Project.StoreDetailsDynamicNodeProvider, Project")]
public ActionResult Index()
{
return View();
}
Seems that the dynamicNodeProvider attribute is ignored in the root node, also when it's defined in attributes.
So the only way to add a dynamic node provider under the root, is either by specifying it on a dummy action etc. or using XML.
An interesting note: the actual difference between defining in XML and attributes is that if it's defined in attributes, it (i.e. the gen. menu items) will be last in the menu, whereas when defined in XML it will be right after the root item (I guess that would be Home), Note that this is still controllable via the Order property in the attributes.
In my Web.Config, I left the siteMapFile empty, relying in what it said in the wiki page, that the default value is ~/Web.sitemap, in fact this is false (I've already corrected that in the updated wiki).
I don't think this behavior should be like this, I do think the MvcSiteMap engine should scan for dynamic node providers just as it scans for dynamic node attributes (here is the issue I posted on site).
I have some experience maintaining Grails apps; now creating a "task management" application as an exercise.
Apparently there is a view dichotomy of Groovy Server Pages versus Controller actions that render a view, as evidenced by this snippet from a URLMappings.groovy example:
static mappings = {
// ..
"/" (view:'/index')
"/login/$action?" (controller: 'login')
"/logout/$action?" (controller: 'logout')
"500" (view:'/error')
}
where user-facing URLs must be mapped to either views (GSPs) or controllers rendering a view, e.g.:
class LoginController {
/**
* Show the login page.
*/
def auth = {
// .. auth logic
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl, rememberMeParameter: config.rememberMe.parameter]
}
}
From a design perspective, how do I choose which method to use? When do I create views with GSPs/taglibs like typical server pages outputting HTML, and when do I map a URL to a controller that renders through a delegate GSP? Can I combine both approaches? Have I oversimplified the options here?
To add to what hvgotcodes said, related to your question, the only time you'd want to map directly to a GSP view is when that view is effectively "static".
By static I mean that it isn't relying on the database or any real calculations for rendering the view. It can still be dynamic in that it relies on tag libraries for dealing with common elements, and things like the "Welcome user" text at the top of pages.
As soon as you want to deal with user-supplied input, looking up database information, manage more complicated URLs, or include calculations, you should be using a controller.
The end goal is that GSPs only contain visual and layout information, as well as the occasional static block of text. But you should always avoid mixing any logic in with the GSP, because it clutters the code and always leads to maintenance headaches later on.
Edit regarding Tag Libraries:
As I wrote below:
Tag libraries are for any logic that is connected to the view, like looping over elements, or toggling the visibility of something. Whenever you are tempted to put code directly into your GSP, it probably should be put in a tag library. Of course, there are always exceptions for one-offs.
So, if you have logic code in your view, that specifically relates to visual or layout content, that should be put in a tag library. A good example is the <sec:ifLoggedIn> tag from Spring Security Core, which can be used to toggle the visibility of an element if the user is logged in. This is much better than writing it manually like so:
<sec:ifLoggedIn>blah blah</sec:ifLoggedIn>
<g:if test="${session.user?.loggedIn}">blah blah</g:if>
Because it makes the purpose clearer (by its title), as well as abstracting the logic away, so if you later need to change the way something works, you only have to change it in one place.
tl;dr:
GSPs - Simplified "static" content
Tags - Reusable dynamic components specifically for visual or layout content
Controllers / GSPs - dynamic content
I don't think it's a dichotomy. GSPs and Controller actions (are intended to) work in tandem, the controller invoking services to load data in preparation for passing that data to the appropriate GSP.
The url mapping stuff is for if you want to break the Grails convention for urls, which is orthogonal to how loading data and displaying data (are supposed) to work.
The only time (IMHO) there is a dichotomy is when developers in a project code functionality inconsistently; i.e. it is certainly possible to give the appearance of a dichotomy.
In my webapplication, I always reply with JSON to AJAX calls.
Thus I find myself doing this on a lot of actions:
if ($request->isXmlHttpRequest()) {
$this->getResponse()->setHttpHeader('Content-type', 'application/json');
return $this->renderText(json_encode($details));
}
Is there a way to get that automatically: anytime the request is AJAX, the content type is JSON?
I was thinking I should use a filter maybe but I am not familiar with filters and maybe there is a better solution.
Any suggestion will be more than welcome.
Thanks,
Dan
The way I solved it:
Create a new class myActions which extends from sfActions. In this class create a new function renderJson($data):
protected function renderJson($data) {
$this->getResponse()->setHttpHeader('Content-type', 'application/json');
return $this->renderText(json_encode($data));
}
Now let your controller class inherit from myActions (instead of sfActions). And at the end of your controller just return $this->renderJson($data);.
(I also did some templating. in the renderJson, if the sf_debug is set in the config, and it's not requested through XmlHttp.)
I'm using a similar technique in one of my projects, but I'd suggest also taking a look at this article regarding iPhone optimization. You can set your routing to accept a format and return the appropriate template based on that. Then you don't need to set the header as it's set by the requested format.
Of course, this means you'll need to create separate template files for each output template, which seems a bother, or use a particular layout file and skip the template. So in the end it may amount to pretty much the same thing (or same amount of code, at least).
Please pardon this newbie question...
In Grails, if I want a partial to be embedded in a layout so that it appears globally, which requires live data, let's say a list of categories, where is the best place to pull the category data to feed it into the view?
I realize this is a very basic question, but I haven't seen this covered in any tutorials yet.
I started this as a comment to Bill James's answer but I figured it might be longer. Bill suggeseted using groovy code inside ${} to make the template (called partial in Rails) work globally:
<g:each in="${ Category.findAll() }" var="cat" />
But, you should not just add code if you dont feel like it might mess up your tidy xml/html. You can always put it in a closure inside a TagLib and thus make it a Tag. The closure must have no parameters, or an 'attr' parameter, or an 'attr' and 'body' parameters but other signatures are invalid.
class CustomTagLib {
static namespace = 'cus'
def categories = { attr, body ->
g.each( in: Category.findAll(), var: attr?.var ?: 'categories' )
}
}
Then you can use that tag into the template with the namespace you chose:
<cus:categories />
Personally I prefer using tags since most of the time it is a reusable code, so it's better for not violating the DRY principle.
You want to put it in grails-app\views\layouts\main.gsp. That's the default layout that most generated code (and likely most examples that you'll see) will use.
Check out the sitemesh section of the grails documentation.
I think you're trying to ask... "How do I feed the category data to the view when I don't know which action caused the page to render, so the action can't add the data to the model?" If that's so, you can use Groovy code directly in the ${} block, such as:
<g:each in="${ Category.findAll() }" var="cat" />
Note that findAll is added to every Model class, and can be called statically (via the classname, not an instance).
Hope this helps