Simple dynamic ajax content replacing with snippets

6 years ago

paolo
Member | 15
+
0
-

In my blog homepage I have, by default, a list of articles, rended by template Article/list.latte, with summaries and a link to the full article:

Homepage/default.latte:

.....
<div id="articles" class="content">
    {includeblock '../Articles/list.latte'}
</div>
.....

Articles/list.latte:

{block #content}
<div class="article-list">
    {foreach $articles as $article}
    <div class="article-list-item">
        <a title="Go to article" n:href="Articles:view, $article->id">
        <div class="article-header">
                <span class="article-title">{$article->title}</span>
                <span class="article-author">{$article->user->username}</span>
                <span class="article-datetime">{$article->creation_time|italianDate}</span>
            </div>
            <div class="article-subtitle">{$article->subtitle}</div>
            <div class="article-summary">{$article->summary}</div>
        </a>
    </div>
    {/foreach}
</div>
{/block}

Now I want to ajaxify the link to the article, and make it load the content of the article inside the “articles” DIV, replacing the list.
How can I do it using snippets (and possibly jquery.nette.js or nette.ajax.js)?

6 years ago

petr.pavel
Backer | 492
+
0
-

Have you read the docs? If you did, what isn't clear to you?

6 years ago

paolo
Member | 15
+
0
-

petr.pavel wrote:

Have you read the docs? If you did, what isn't clear to you?

Of course I read them. And I've seen the Fifteen example, at which the docs refer. But this example use a component. The snippets in the template refer explicitly to the control rendering ({control fifteen}) or to some of its properties ({$presenter['fifteen']->round + 1}). This way it is rather simple.

But in my case I want to replace the contents of the snippet with the contents of another presenter template.
As a test, I tried this (with jquery.nette.js):

default.latte:

<a n:href="view, 1" class="ajax">Article #1</a>
.....
{snippet articles}
  {ifset #content}
    {include #content}
  {/ifset}
{/snippet}

view.latte:

{block #content}
.......
{/block}

HomepagePresenter.php:

public function actionView($id = 0)
{
  if ($this->isAjax())
      $this->invalidateControl('articles');
}

While the non-ajax version (without class="ajax") displays the article in another page as expected, with the ajax version, I only see the spinner for a few seconds, but nothing gets displayed.
Maybe I'm missing something very basic which for you is fairly obvious…
A very simple working example may help me very much.

Thank you for your attention.
Paolo

Last edited by paolo (2012-09-04 14:10)

6 years ago

Vojtěch Dobeš
Member | 1317
+
0
-

I guess the problem is, that you want both templates to be used without clear connection between them. actionView will use view.latte, but won't use default.latte (how should know to use it?). And default.latte shows {#content} only if set (if the shown template is complete, I suppose the snippet is empty?).

6 years ago

Vojtěch Dobeš
Member | 1317
+
0
-

I believe the best-practice is to have {include #content} in @layout.latte. Then every view template contains {block #content} with appropriate content. If you wrap the {include} in snippet and invalidate it e.g. in beforeRender() method, it should work pretty well with ajaxification.

6 years ago

paolo
Member | 15
+
0
-

vojtech.dobes wrote:

I guess the problem is, that you want both templates to be used without clear connection between
them. actionView will use view.latte, but won't use default.latte (how should know to use it?)

Thank you for your hints.
In fact what I was missing was to declare view.latte as an extension of default.latte. I also followed your advice to put the invalidation code in BeforeRender, and now it works as expected.
I read again the docs, and realized that maybe the best thing to do is to build the application without ajax until all pages look and work as planned, then ajaxify it by adding snippets and class="ajax" to the links. This also grants that all pages look the same if loaded without ajax (e.g. by pressing the reload button in the browser). Very simple and very good!
The only other things to take care of, are to explicitly declare {include '@layout.latte'} in the homepage, to assure that the base template is included in “child” templates too and, if you have ajax links inside some snippet, to provide some javascript to ajaxify the links loaded by ajax (using a different selection mechanism other than the ajax class, to avoid affecting the already ajaxified links).

Last edited by paolo (2012-09-06 19:00)

6 years ago

Vojtěch Dobeš
Member | 1317
+
0
-

As you say, that's great feature of Nette, that you can write application without AJAX, and then easily ajaxify it by few extra simple lines of code.

Ajaxifying the links loaded via AJAX is handled by nette.ajax.js, you might wanna try that one. It's no problem to have all links with CSS class ajax – it makes sure that links have only ajaxifying callback attached.

6 years ago

paolo
Member | 15
+
0
-

Now I'm using nette.ajax.js and it works very well. No need to care about ajax links loaded via ajax. Thanks.
Only, I modified the spinner extension to be displayed at the center of the document:

createSpinner: function () {
    return $('<div>').attr({
        id: 'ajax-spinner'
}).css({
        display: 'none',
        position: 'fixed',
        left: '50%',
        top: '50%'
});
},

Note that setting the style attribute instead of using css() does not work.
One question: what is the scrollTo extension?

6 years ago

Vojtěch Dobeš
Member | 1317
+
0
-

Yeah, I have to fix that.

scrollTo adds behavior, that page scrolls to first invalidated snippet.