TYPO3 / News: Lazy Loading von Nachrichten per AJAX

Seitennavigation bzw. Pagination sind ein sicherer Weg zum durchstöbern von vielen Datensätzen wie zum Beispiel Nachrichten auf einer Webseite. An immer mehr Stellen im Netz begegnet man jedoch dem coolen Bruder „Lazy Loading“, der beim Runterscrollen einfach weitere Posts, Nachrichten oder Ähnliches nachlädt.
In diesem Artikel möchte ich euch zeigen, wie ihr Lazy Loading von Nachrichten-Datensätzen der Extension „news“ (auch bekannt als „Versatile News“ oder „tx_news“) in einem TYPO3 6.x+ System realisieren könnt.

Die Extension tgm_lazynews

Zunächst wollte ich in diesem Beitrag jetzt Schritt für Schritt aufzeigen was für eine Extension ihr euch erstellen müsst um Lazy Loading News für eure TYPO3 Seite zu realisieren. Dabei kam mir dann aber die Idee, mach die Extension einfach fertig und lad sie ins TER.

Installiert einfach nur die Erweiterung, bindet das statische Typoscript Template ein und gebt unbedingt die StoragePID eurer News in den Konstanten ein.

Die Extension besteht einfach nur aus einem Controller, einem Model und einem Repository welche „News“ erweitern. Das Repository hat eine Funktion zum Suchen von Datensätzen nach Offset und weiteren Bedingungen wie zum Beispiel Kategorien.

Der Controller bietet eine Action die als Argumente das Offset, das Limit, das Model [STRING „news“] sowie optionale constraints erwartet. Ist alles vorhanden wird das Repository aufgerufen und die Datensätze werde an den View Ajax.html der Extension weitergeleitet. Das Template muss von euch soweit angepasst werden, dass die Ausgabe mit der normalen Ausgabe ohne Lazy Loading identisch ist.

AJAX Abrufe

Die Extension erstellt die typeNum Seite 6363 über die ihr eure AJAX Abrufe machen könnt. Des Weiteren bindet die Erweiterung eine Javascript Datei mit der folgenden Funktion ein:

var lazy_endReached = false;
var lazy_loading = false;

function lazyLoadNews(model, selector, offset, limit, constraints) {
    
    if(typeof(constraints)==='undefined') constraints = false;
    
    $loader = '<div class="ajax_loader" style="padding: 30px 0; text-align: center; float: left; width: 100%;"><img src="typo3conf/ext/tgm_lazynews/Resources/Public/Image/ajax-loader.gif" alt="Loading..." /></div>';
    selector.append($loader);
    
    var data = 
        { 
            "type" : "6363", 
            "tx_tgmlazynews_ajax[action]" : "ajax", 
            "tx_tgmlazynews_ajax[controller]" : "News",
            "tx_tgmlazynews_ajax[model]" : model, 
            "tx_tgmlazynews_ajax[offset]" : offset,
            "tx_tgmlazynews_ajax[limit]" : limit,
        };
    
    // Constraints seit 0.2.0 per Typoscript, siehe TER
    //if(constraints) data['tx_cedlazynews_ajax[constraints]'] = constraints;
    
    $.ajax({
        type: "GET",
        dataType: "text",
        url: $('base').attr('href'),
        data: data,
        success: function (d) {
            $('.ajax_loader').remove();
            
            if(d=='end') {
                lazy_endReached = true;
                return;
            }
            $(d).appendTo(selector).fadeIn();
            lazy_loading = false;
        }
    });
}

Die Funktion erwartet das gewünschte Model als String (news), einen jQuery Selektor (der News-List Container dem die Lazy Loaded News angehängt werden sollen), das Offset, das Limit und optionale, weitere Bedingungen.

Ihr könnt nun ganz einfach eine eigene Javascript Funktion schreiben, die beim erreichen des Endes euer Nachrichtenliste die Funktion lazyLoadNews aufruft. Euer Script könnte beispielsweise so aussehen:

$(document).ready(function() {
   if($('.news-list-view').length) initLazyLoading(); 
});

function initLazyLoading() {

    $(window).scroll(function() {
        
        // Lazy load when user reaches the end of the news-list
        if($(window).scrollTop()+$(window).height() > $('.news-list-view').position().top+$('.news-list-view').outerHeight(true) && lazy_endReached==false && !lazy_loading) {
            
            // The offset can be the count of currently visible articles
            var offset = $('.news-list-view .article').length;
            
            // Set lazy loading true to prevend multiple lazy loading at the same time
            lazy_loading = true;

            // SEIT 0.2.0 IM TYPOSCRIPT SIEHE TER
            //var contraints = {'categories' : "1,2,3"};
            
            // Call the function
            // Model = news, jQuery selected news-list-container, offset, limit
            lazyLoadNews('news',$('.news-list-view'), offset, 5);
        } 
    });
}

 

25 Kommentare

  • Hallo Paul,

    vielen Dank für den prima Ansatz. Ich habe jetzt versucht, die Extension unter 7.4 einzusetzen. Die Scripte habe ich soweit übernommen und den Section-Bereich des NewsList-Templates in das Ajax-Template kopiert. Was mir jetzt nicht ganz klar ist: Wie stelle ich das News-Plugin ein? Ich habe herausgefunden, dass die LazyLoading-Extension sich das Limit aus den Plugin-Einstellungen holt (Maximale Anzahl anzuzeigender Datensätze). Wenn ich nun runterscrolle wird auch tatsächlich ein Axajaufruf abgesetzt. Leider erhält das $.ajax-Object bei „success“ für $d ein „end“ zurück. Wenn ich den Controller richtig verstehe, heißt das, dass das abgerufene News-Array leer ist. Das wäre es, wenn das Plugin nur die zwei bereits angezeigten zurück gibt. Aber so soll es sicher nicht laufen, oder?

    Eine weitere Frage: Kann ich das Ajax.html auch unter fileadmin ablegen?

    Danke und viele Grüße

    • Hallo Martin,

      danke für dein Feedback. Die Anzahl der zu ladenden News wird momentan nicht aus dem News Plugin übernommen sondern im Javascript konfiguriert (siehe Beispiel-Script Parameter). Dies automatisch aus dem News Plugin zu holen ist eine gute Idee die ich für ein Extension Update in Betracht ziehen werden.

      Wenn ich Dich richtig verstehe, werden momentan von News schon zwei Nachrichten ausgegeben und es gibt eigentlich noch weitere, ja? Hast Du mal überprüft ob Dein Script die richtigen Parameter an die JS Funktion lazyLoadNews übergibt?

      Zu Deiner Template Frage: Ja, Du musst lediglich den Pfad zum Template in den Konstanten der Lazy News Extension ändern.

      Um Dein Problem möglichst schnell zu lösen kannst Du mich auch gerne unter teamgeist_medien_pab per Skype kontaktieren.

      Viele Grüße aus Schwerin

  • Hallo Paul,

    ich habe auch mal eine Frage zu der Extension. VOrabgesagt funktionieren tu sie echt gut, allerdings habe ich das problem wenn ich eine newsbeitrag lese und dann zurück gehe springt es ja immer zum Top beitrag ich möchte aber das ich wieder da zurückkomme wo ich war. Ist das möglich ?

     

    Weil es ist ja nicht gerade benutzerfreundlich wenn ich jedesmal die ganze sache wieder nach untenscrollen muss.

     

    Vielen Dank und Gruß Michael

    • Hallo Micha,

      das ist in der Tat nicht besonders Benutzerfreundlich. Dies stört mich auch bei Facebook, wo es dasselbe Problem gibt. Eine Lösung hierfür wäre ein zusätzlicher GET Parameter verbunden mit ein wenig JS. Ich schreibe mir das mal auf die Liste, werde aber wohl nicht zeitnah dazu kommen eine Lösung zu implementieren :-(

  • Hallo Paul,

    ich war froh deine Extension gefunden zu haben und hatte aber direkt ab start ein Problem welches sich nicht Lösen lassen will:

    Und zwar kriege ich nach der Installation, dem includen des Templates und dem Beispiel Javascript folgenden Fehler, welcher anscheinend schon im Controller entsteht: „Error. Missing Arguments Offset, Limit and Model“

    Das Beispiel Javascript habe ich ebenfalls soweit angepasst.

    Ich Arbeite momentan mit der Typo3-Version 7.6.2, sehe ich den Wald vor lauter Bäumen nicht ? 🙂

     

    • Hey Raoul,

      irgendeiner der benötigten Parameter wird nicht vernünftig an den Extbase Controller übergeben. Wenn Du da nicht weiterkommst kannst Du mich gerne mal bei Skype anschreiben und wir schauen mal gemeinsam (teamgeist_medien_pab).

      Gruß aus Schwerin

      • So, nachdem ich mit Paul knapp eine Stunde nach dem Fehler gesucht habe, sind uns zwei Punkte aufgefallen die zu meinem Fehler führten.

        1. Meine Root-Seite ist ein Shortcut auf die erste Unterseite, was dafür sorgte dass der Server eine Weiterleitung erzeugte die nicht mehr die passenden Parameter für die News-Liste enthielt. Das Problem lösten wir über eine kleine Änderung in der Extension Js. (ext/tgm_lazynews/Resources/Public/Javascript/tgm-lazyload.js)

        Anpassen:
        Zeile 4: function lazyLoadNews(model, selector, offset, limit, pageid) {

        Hinzufügen:
        vor Zeile 24: if(pageid) data[‚id‘] = pageid;

        Somit habt ihr also nun die Möglichkeit eure Startseiten-ID im Javascript anzuhängen:
        lazyLoadNews(’news‘,$(‚.news-list-view .medium-8‘), offset, 2, 3);

        2. Ich hatte meiner Seite das LazyNews Plugin hinzugefügt und dort die StoragePID eingetragen.

        Hier muss gesagt werden, dass das LazyNews Plugin nicht auf der Seite integriert werden muss. Die Storage PID wird über die Angabe im Constant-Editor an das Plugin übergeben.

        Paul, ich danke dir noch mal für deine Hilfe, dass Plugin läuft jetzt einwandfrei und ich kann mich weiter ans Layout machen 🙂

        Grüße,
        Raoul

  • Hallo,

    bin begeistert von der Extension 🙂
    Allerdings habe ich noch folgende Probleme, evtl kann jemand helfen?

    1. Übersetzungen funktionieren nicht

    in der tx_news habe ich die Übersetzungen so angelegt:

    plugin.tx_news {
    _LOCAL_LANG {
    de {
    more-link = mehr erfahren
    }
    }
    }
    

    Ajax.html:

    &lt;n:link newsItem="{newsItem}" settings="{settings}" class="btn btn-default" title="{newsItem.title}"&gt;
    &lt;f:translate key="more-link" extensionName="News" /&gt;
    &lt;/n:link&gt;
    

    Der more-link enthält allerdings den englischen Text…

    2. der Teaser/Bodytext wird nicht ausgegeben

    • Achso, vielleicht auch relevant (sorry, vorhin vergessen):

      TYPO3 7.6.9
      tx_news 4.2.1
      tgm_lazynews 0.2.0

      Und hier noch ein neuer Versuch, den Auszug aus Ajax.html anders darzustellen:

      &lt;n:link newsItem="{newsItem}" settings="{settings}" class="btn btn-default" title="{newsItem.title}"&gt;
      &lt;f:translate key="more-link" extensionName="News" /&gt;
      &lt;/n:link&gt;
      

      Danke und Grüße
      Kerstin

      • Ok, Punkt 2 ist gelöst. Die Einstellungen für cropMaxCharacters wurden nicht übernommen. Bleibt das Problem der Übersetzung…

        • Hallo Kerstin,

          ich könnte mir vorstellen, dass die AJAX Schnittstelle bisher noch keinen Language Parameter entgegen nimmt und deshalb nicht erkennt in welcher Sprache das Template gerendert werden muss. Das muss ich die kommenden Tage allerdings nochmal genauer überprüfen. Ist englisch in Deinem System die Standardsprache?

        • So funktioniert es jetzt wenigstens:

          <span class="keyword2">plugin</span><span class="ts-operator">.</span><span class="other ts-value">tx_tgmlazynews</span> <span class="other ts-value">{</span>
          <span class="whitespace">   </span><span class="other">settings</span><span class="ts-operator">.</span><span class="other ts-value">cropMaxCharacters</span> <span class="ts-operator">&lt;</span> <span class="keyword2 ts-value">plugin</span><span class="ts-operator">.</span><span class="other ts-value">tx_news</span><span class="ts-operator">.</span><span class="other ts-value">settings</span><span class="ts-operator">.</span><span class="other ts-value">cropMaxCharacters</span>
          <span class="whitespace">   </span><span class="other">settings</span><span class="ts-operator">.</span><span class="keyword ts-value">_LOCAL_LANG</span> <span class="ts-operator">&lt;</span> <span class="keyword2 ts-value">plugin</span><span class="ts-operator">.</span><span class="other ts-value">tx_news</span><span class="ts-operator">.</span><span class="keyword ts-value">_LOCAL_LANG</span><span class="ts-operator">.</span><span class="other ts-value">de</span>
          <span class="ts-operator curly-bracket">}
          

          Danke nochmal für das Plugin 🙂

          • Danke für die Antwort 🙂 DE ist die Standardsprache.
            Was mache ich beim einfügen der Skripte hier falsch?

  • Hey Kerstin du machst da nichts verkehrt der fkking Comment-Editor macht nur Mist hab den jetzt mal deaktiviert.

  • Jetzt habe ich noch eine Frage, hoffe das ist ok?

    die News können bei mir über Kategorien gefiltert werden (Category Menu der tx_news). Das Category Menu sowie die Listenansicht der News befinden sich auf der gleichen Seite. Klicke ich auf eine Kategorie und scrolle dann nach unten, werden News aus allen Kategorien nachgeladen, und nicht nur aus der ausgewählten.

    Noch einen Tip für mich? Danke!

    Grüße
    Kerstin

  • Hallo Kerstin,

    momentan ist noch keine automatische Filterung von Kategorien eingerichtet. Es ist jedoch ein Feature welches ich für zukünftige Version mal mit aufnehmen werden. Bis dahin kannst Du die News per Typoscript manuell filtern wie hier beschrieben:
    https://docs.typo3.org/typo3cms/extensions/tgm_lazynews/#filtering-of-news-by-constraints-conditions

    Im Prinzip müsstest Du einfach nur den GET Parameter des aktuellen Kategoriefilters auslesen und kannst dann den Filter auf Lazy News anwenden. Eine automatische Filtererkennung ist aber auf jeden Fall für die nächste Version notiert.

  • Hi Paul,

    erstmal: Danke für das Plugin! Super praktisch, wenn man News Artikel via AJAX laden will.

    Mittlerweile gehen die Constraints ja via TS, hier ist ein Beispiel wie man nach einer Kategorie filtert:

    plugin.tx_tgmlazynews {
    settings {
    lazy_constraints {
    0 {
    property = categories
    #Hier die UID der Kategorie einfügen
    value = XX
    intval = 1
    operator = contains
    }
    }
    }
    }

    Einen kleinen Nachteil hat das noch: Bei mehreren Constraints hast Du aktuell nur eine „AND“ Verknüpfung vorgesehen. Wäre natürlich praktisch wenn man das auch im TS setzen könnte, ich kann mir z.B. vorstellen das eine OR Verknüpfung häufiger vorkommt.

    Nochmal DANKE!!!

    LG

    Florian

    • Hallo Florian und vielen Dank für Dein Feedback! Ich werde deinen Vorschlag probieren ins nächste Update mit auf zu nehmen. Die Filterung von Kategorien soll dann übrigens (optional) automatisch anhand der News-GET-Parameter erfolgen.

  • Hallo Paul,

    erstmal Danke für deine Extension. Ich hab versucht die auf Typo3 7.6.18 zu installieren. Leider greift die Extension nicht. Statisches Template ist nach dem von News inkludiert. Das Javascript der Extension wird vor dem inkludiert. Mein Javascript wird im Header inkludiert. Dennoch kommt die normale News-List Ansicht und nicht die Ajax.html. Muss die Extension noch für 7.6. umgeschrieben werden?

    Grüsse

    Bernd

    • Hallo Bernd,

      Ajax.html ist ja nur das Template für die News die nachgeladen werden wenn ans Ende der Liste gescrollt wurde. Hast Du mal geschaut ob das Javascript beim erreichen der letzten News einen AJAX Aufruf macht um News nachzuladen?

      Gruß Paul

  • Sebastian Krings

    Hallo Paul,

    sehr schöne Extension! Ich hab anfangs auch ein kleines Brett vor dem Kopf gehabt, aber schließlich funktioniert es nun wunderbar.
    Die Frage war zuerst, ob und wie ich das Skript am besten auf der Seite einbinde. Für alle, denen es vielleicht hilft – da das Skript ohnehin nur aktiv wird, wenn der Container auf der Seite vorhanden ist (also wenn ein News-Plugin vom Typ List auf der Seite inkludiert ist), kann man es einfach als JS-Datei ins Grundtemplate einbinden.

    Viele Grüße

  • Sebastian Krings

    Das Zurückspringen zu der angeklickten meldung habe ich nun, wie vom Verfasser der Extension angedacht, über zwei GET-Parameter gelöst:

    Im NewsController von tgm_lazynews wird zunächst mit assignMultiple der Offset mit an den View übergeben:

    'items' => $offset
    

    Im Template kann man dann im Link mit

    configuration="{additionalParams:'&tx_news_pi1[items]={items}'}
    

    den Offset an die Detailansicht der Meldung übergeben. Allerdings wusste ich mir (noch) nicht anders zu helfen, als direkt in die Original News-Extension einzugreifen, um die weiteren Schritte zu bewerkstelligen:

    Im NewsController in der detailAction wird der GET-Paramter nun wie folgt abgefangen:

    if ($this->request->hasArgument('items')) {
    			$items = $this->request->getArgument('items');
    		}
    

    und in $assignedValues eingefügt, um mit an den View weiteregegeben werden zu können:

    $assignedValues = [
         'newsItem' => $news,
         'currentPage' => (int)$currentPage,
         'demand' => $demand,
         'items' => $items
    ];
    
    So kann man ihn wiederum an den View ins Detail-Template übergeben und in den Backlink einfügen, der dann wie folgt aussieht:
    
    [html]
    <div class="news-backlink-wrap">
    	<f:link.page pageUid="{settings.backPid}" additionalParams="{lastuid:'{newsItem.uid}', tx_news_pi1:{items:'{items}'}}" noCache="1">
    	    <f:translate key="back-link" />
    	</f:link.page>
    </div>
    [/html]
    
    Hier wird übrigens auch die news uid mitübergeben, um nachher in der Liste zur entsprechenden Stelle springen zu können.
    
    Damit die Liste beim zurückspringen nun so viele News-Einträge lädt, wie zuvor, wird der Offset (items) wiederum als Argument gleich in der News Extension im NewsController abgefragt, und zwar in der listAction. Ist der Wert als Parameter übergeben worden, wird der Limit-Wert im demand-Objekt entsprechend angepasst und mit 10 (oder wie viele NewsItems ihr in eurer Liste standardmäßig ladet) addiert:
    
    [php]
    if($this->request->hasArgument('items')) {
    	$demand->setLimit($this->request->getArgument('items')+10);
    }
    

    Jetzt erscheint schonmal die News-Liste in dem Umfang, wie sie verlassen wurde.

    Um nun an die entsprechende Stelle zurückzuspringen bzw. die betreffende News in den Blick zu holen, noch etwas JavaScript, und zwar im selbst angelegten Skript, mit dem auch LazyLoading aufgerufen wird:

    $(document).ready(function() {
    	if($('.news-list-view').length) initLazyLoading();
    
    	var lastUid = getUrlParameter('lastuid');
    	if (typeof lastUid !== 'undefined') {
    		var searchedElement = $("a[href*="+lastUid+"]").offset().top;
    		console.log(searchedElement);
    		$('html, body').scrollTop(searchedElement-200);
    	}
    ...
    

    Um den Parameter aus der url abzufragen (var searchedElement) noch eine entsprechende Funktion einfügen (gefunden bei StackOverflow):

    var getUrlParameter = function getUrlParameter(sParam) {
    	var sPageURL = decodeURIComponent(window.location.search.substring(1)),
    		sURLVariables = sPageURL.split('&'),
    		sParameterName,
    		i;
    
    	for (i = 0; i < sURLVariables.length; i++) {
    		sParameterName = sURLVariables[i].split('=');
    
    		if (sParameterName[0] === sParam) {
    			return sParameterName[1] === undefined ? true : sParameterName[1];
    		}
    	}
    };
    

    Voilà. Ich hoffe, ich kann damit jemandem weiterhelfen. Danke für die super Extension und viele Grüße!

    • Hallo Sebastian und vielen Dank für den guten Lösungsvorschlag. Ich hoffe das ich bei der nächsten Aktualisierung der Erweiterung einige deiner Einsätze direkt in die Erweiterung implementieren kann. Lediglich der Eingriff in den direkten Code der Extension „news“ sollte dabei vermieden werden um „updatesicher“ zu bleiben. Trotzdem erstmal ein sehr guter Schritt nach vorne!

  • Sebastian Krings

    Hallo Paul, ja, das ist in der Tat noch unschön. Da ich aber noch nicht genaug Know-how für einen anderen Weg habe, muss erstmal die „Quick-and-Dirty“-Lösung herhalten. Ich bin gespannt auf das nächste Update.

Schreibe einen Kommentar zu Kerstin Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Highlighting von Codes ist mit den Tags  [ts], [php], [html], [javascript], [xml] oder [code] möglich.