Jump to content

Multiple views for templates


mindplay.dk

Recommended Posts

This may be another one of those ideas that are answered mostly along the lines of "you can already do that by..." - but I'm going to post it anyway, just to see what you make of it.

I wonder if it would make sense to support more than one view (php file) for the same template?

Currently, php views/files are tied 1:1 to Templates - I realize you can tie more than one Template to the same file, but what I have in mind here is the opposite: tie more than one file to the same Template, and make it optional which one to render.

So with this change, $page->render() would render the default view, and other named views could be rendered e.g. using $page->render('summary') which would be useful for example when you're rendering related pages (e.g. from a page selector field) from within another view, as these are typically not displayed in the exact same way as when they render on a dedicated page.

Thoughts?

  • Like 1
Link to comment
Share on other sites

Greetings mindplay.dk,

I've thought about this before as well, so I'm definitely interested.

Describe the situation in which you would want this, then let's explore it...

Depending on what you're trying to accomplish, I wonder whether conditional logic inside a template could take care of a lot of the possible scenarios.

For example, let's say you wanted to have a different view depending on who is viewing the page, you could just say... If the visitor is ____, then render this section of code, if not, render this other section.

The same idea could apply to detecting how they visitor got the the page, or if it's a form depending on the GET or POST data.

With this approach, you could have a set of fields for the template, and only render certain ones based on the conditional test.

I'd be curious to hear the particular scenarios you need to deal with!

Thanks,

Matthew

Link to comment
Share on other sites

I agree that this can already be done with code - no question.

To use a practical example, let's say we have two templates, "author" and "blog-post". 

The "blog-post" template has a reference field, crediting one or more authors for the post.

An "author", when rendered as a page, is going to display detailed profile information about the author, and maybe list his most recent posts.

A "blog-post", when rendered as a page, is going to display who the authors are, but probably in a short summary format.

Now currently, the way I would implement the template for "blog-post", is I get the list of authors, and I loop over them inside the "blog-post" template. As opposed to calling $author->render() for each author, because that would render with the full template with all the details and listing other posts.

So you've got the "blog-post" template now in charge of actually rendering bits and piece of "author" pages, but not using it's template, and avoiding the render() method, because it renders a full document view, where in this case, all I want is a partial.

What I'm seeing, is that you actually have multiple views of the same information in different contexts.

Let's say we also have an "article" template, which also has a list of "author" references - now I can either duplicate the snippet of code that renders the authors in the compact format, or I can move that code into an external include file, e.g. a separate view.

But there's currently no way to formally associate an alternate view with a template.

Another example would be a template that requires two different views - for example, an "article" template might have both a "teaser" view and a different (default) full article view. Currently, we can model that with an extra URL argument, and an if/else statement in the template, making the decision about which view to render.

But again, there is no formal way to associate the alternative view(s) with the template.

I'm not saying there has to be - it just seems like a rather common requirement, and possibly one of those things where it might be beneficial for a non-programmer (designer) to be able to add multiple views for a template, without having the programmer (me) become a bottleneck in those situations...

  • Like 2
Link to comment
Share on other sites

Greetings mindplay.uk,

Now I see what you mean. I was imagining different uses.

What you're talking about reminds me of the hardwired features in CMSs like Joomla, where there is a "blog" view and "list" view for articles (pages). After years of using Joomla, I was happy NOT to have that system any longer.

In ProcessWire, I simply create a second template whose function is to pull the necessary fields from the page(s) I need for my "list" view. Yes, it's a second template, but it's completely flexible how I compose that "list" view. By contrast, the Joomla system essentially forces the "list" view to be composed of certain "fields" and pre-determined layouts.

In ProcessWire, we define where someone enters the "list" and "full" views, so it makes sense to me that we would use a separate template for that. Thinking this through, if there were essentially two separate views for each template, I suppose we could have a way to define which one is rendered from which entry point.

But then there's the issue of how many possible alternate views might exist. I'm working on a site now where -- in addition to the "list" and "full" views -- I also have views for "featured" pages. I can imagine even more variations.

Always interesting...

Thanks,

Matthew

  • Like 2
Link to comment
Share on other sites

Hmm, one good observation there, is the fact that, even if there was a way to have multiple views for a template, you still need to select them somehow - so, as a minimum, you would still need something like $subpage->render('subview') which is really just indirectly specifying the view. Which negates the value of defining it via the admin interface, as you would still need to change the template - or add a view dropdown to the page reference field config, as you suggested. I think that starting down that path, this can grow out of proportion to the benefit you get from this, very quickly, so I'm starting to not like this idea so much.

However, what do you think about the following simple solution: have the render() method take an optional argument specifying a relative path, from the current template file?

So if your template file is "blog/view.php", calling render('author.php') renders "blog/view.php" - while an absolute path such as render('/blocks/author.php') is "absolute" from the templates root-folder.

How does that sound?

  • Like 1
Link to comment
Share on other sites

Not what you have in mind, but this could also be solved if the render() method would accept a parameter and pass it to the $page object template being rendered. Then you would just have to check for it on your template, and do whatever you would want with it.

edit: :D :D unbelievable!!

edit2: Just to make it clear that i posted this before your last post appeared to me, but my idea is that render() accepts any kind of parameter to be used as you wish in the template.

Link to comment
Share on other sites

You can already pass parameters to child templates - it's also possible for child templates to look at their parent template and obtain information from there. So I think there are already enough ways to communicate between templates.

(Myself I use a global Document object that carries metadata and other things that need to be accessible/shared between templates, as opposed to adding data to Page objects - which feels dirty to me, since Page objects are persisted; I've never held any love for mixing persistent and transient data in the same object.)

What I'm looking for, is a way to avoid having that conditional logic in the child template - and instead, having that control in the parent template. And of course you can do that with include() or require() now, but then child templates don't run in an isolated scope - variables from the parent template become accessible to the child template, and the child template can overwrite values set by the parent template, which is error-prone... render() ensure each page executes in it's own scope, and lets you be explicit about what you want to access or share and when...

Link to comment
Share on other sites

I kind of like the current possibility to change template file on the fly. If that is common need then it might be good to have it as a argument for render. Makes nice and clean code:

$news->render("snippets/list.php")

$page->render("themes/mobile.php")

Etc.

  • Like 2
Link to comment
Share on other sites

I don't like the idea of overwriting any persistent property with a transient value, in any model. It's just extremely bad practice.

Having the ability to specify the template on render makes more sense - after all, you're not changing the template of the page, you're changing which template gets rendered. Not the same thing, unless you also plan on calling save() for some odd reason ;)

  • Like 1
Link to comment
Share on other sites

You can already pass parameters to child templates - it's also possible for child templates to look at their parent template and obtain information from there. So I think there are already enough ways to communicate between templates.

I'm not sure to understand this. How do you do it? And what do you mean by child and parent templates?

Link to comment
Share on other sites

I'm not sure to understand this. How do you do it? And what do you mean by child and parent templates?

http://processwire.com/talk/topic/941-accessing-values-from-%E2%80%9Cparent%E2%80%9D-template-when-page-is-rendered-within-another-template/

http://processwire.com/talk/topic/1510-templatedisplay-mode/

There's more but can't find. So you can also do:

$somepage->contextpage = $page;
echo $somepage->render();

And in the $somepage template

if($page->contextpage->id == 1001){ ..
  • Like 2
Link to comment
Share on other sites

However, what do you think about the following simple solution: have the render() method take an optional argument specifying a relativepath, from the current template file?

This sounds like a good solution to me. I can see this being useful. Though I'd probably want to make any relative paths relative to the /site/templates/ dir, just to prevent any ambiguity about the starting point. 

  • Like 5
Link to comment
Share on other sites

Greetings.

Are we just talking about setting two different paths for the same template, then displaying a different layout/fields for each one?

Let's say I have "list" view that combines several different templates, say for an author site. I might have different templates for each subject category (history, fiction, comic novels, how-to, etc).  But then I want to have a list page that displays the latest five entries from each of those categories.

I definitely see how it would be good to be able to specify relative paths as Ryan suggests.  But I just wanted to say that there is already terrific versatility and power in the way Ryan conceived ProcessWire -- making more than one template for the layouts/fields you want to display.

Thanks,

Matthew

Link to comment
Share on other sites

To clarify, by "parent template", I simply mean the template of the Page object that was dispatched - and by "child template", I mean the templates of any other Page objects related to the parent.

What I'm currently doing, is either rendering child content directly from within the parent template, or using include/require to pull in an include file used to render a child template.

What I'd like to be doing, is simply loop over child Page objects and call render() - as explained, that rarely works, because child Page objects usually are a full page; they're not designed to render inline - with the exception of things that are strictly child Pages, never themselves rendered as a parent, i.e. never dispatched directly.

Link to comment
Share on other sites

Do you think it would make sense for the render() method to also accept a second argument: an array of values to pass to the template of the sub Page?

As explained, I'm really not keen on adding transient values to Page objects - which would be (and currently is) the only other way to pass options to sub-templates...

  • Like 1
Link to comment
Share on other sites

Do you think it would make sense for the render() method to also accept a second argument: an array of values to pass to the template of the sub Page?

In my opinion it would make sense and it would be very useful.

Link to comment
Share on other sites

It's how most view-engines work, I suppose - the render() method usually takes the name of the view and the data to render.

I'm not sure precisely how we would get around the fact that numerous variables are already in scope by default, however?

If you pass array('page'=>'foo') will the "real" $page get overwritten? do you lose that variable? or do you get an Exception? 

Perhaps better would be to pass additional view data as a single variable rather than extracting it before rendering? e.g. $view or $data or someting?

Link to comment
Share on other sites

I'm not sure precisely how we would get around the fact that numerous variables are already in scope by default, however?

If you pass array('page'=>'foo') will the "real" $page get overwritten? do you lose that variable? or do you get an Exception? 

I would think that whatever you pass in would overwrite any defaults already present? That way, if you want to override something, you can. If you don't want to, then you leave it out. I guess the question is whether there is any value to overwriting the variables that are already populated in the template. The only one that seems like it could have value would be $page. Until I think about it a little more and… what would be the point of calling $page->render(...) if you were going to overwrite the $page in it. :) It would be more logical to just find the $page you want and call its render() method instead. Maybe throwing an Exception does make sense. Though need to think about it a little more. 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...