MVC-Architektur in Tntnet (Variante III.)

Standard

In der Vergangenheit habe ich bereits zwei Varianten (hier und hier), wie man mit Tntnet ein MVC-Konzept umsetzen kann, vorgestellt. Heute zeige ich eine dritte Möglichkeit.

Diese nutzt eine Template-Engine namens „NLTemplate“. Der Projekt-Code ist kompakt und überschaubar, herunterzuladen bei GitHub. Installation und  Benutzung sind ausreichend dokumentiert, daher gehe ich hier nicht weiter darauf ein, sondern beschreibe lediglich die Benutzung in Tntnet.

Auf Eines sei aber noch vorher hingewiesen : das Template-Engine läuft, wenn es das angegebene Template-File nicht laden kann, in einem segment foult. Das bringt den gesamten Application-Server von Tntnet zum Absturz. Deshalb habe ich den Code für mich geforkt und angepasst. Jetzt wirft bei mir die Template-Engine Exception, wenn sich das Template-File nicht öffnen lässt. Der Tntnet-Application-Server kann damit sauber umgehen.

Durch die Verwendung von NLTemplate ist die Trennung zwischen View- und Controller-Code noch stärker, als es mit Bordmitteln von Tntnet möglich ist. Die Template-Files enthalten keinerlei interpretierten Code oder irgend welche „Magic“ mehr. Der zweite Vorteil, den man aber auch als Nachteil sehen kann, ist dass die Template-Files zur Laufzeit geladen werden. Das bedeutet, dass man die Template-Files ändern kann ohne den kompletten Code neu übersetzen zu müssen. Der dritte Vorteil ist, dass man den Precompiler ecppc nicht mehr braucht. Das vereinfacht den Build-Prozess.

Hier zu meinem Beispiel : es gibt zwei Template-Files. „site_skeletal.nlt“ ist das Template, dass die Seiten-Struktur abbildet, die auf jeder Seite gleich bleibt.

<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>{{ site_title }}</title>
    </head>
    <body>
        {{ main_content}}
    </body>
</html>

Der Bereich „{{ main_content}}“ wird ersetzt durch den individuellen Teil der Seite. Die zweite Template-Datei sieht so aus:

<h1>Use the NLTemplate system</h1>
You can generate items with loops:
<ul>
{% block town_items %}
    <li>Title: {{ town_name }}</li>
{% endblock %}
</ul>
You can use this template system without a pre compiler.

Dieser Inhalt wird in das obere Tamplate eingefügt. Der Teil, der mit dem Schlüsselwort „block“ gekennzeichnet ist, kann in einer Schleife beliebig generisch wiederholt werden. Mit den doppelt geschweiften Klammern werden Schlüsselwörter gekennzeichnet, die durch einen beliebigen Inhalt ersetzt werden können.

Jetzt zum Controller-Code:

#include <tnt/ecpp.h>
#include <tnt/httprequest.h>
#include <tnt/httpreply.h>
#include <tnt/httpheader.h>
#include <tnt/http.h>
#include <tnt/data.h>
#include <tnt/componentfactory.h>
#include <stdexcept>
#include <ostream>
#include <vector>

#include <NLTemplate/NLTemplate.h>

log_define("component.alternativ_hello")

namespace
{
class _component_ : public tnt::EcppComponent
{
    _component_& main()  {
        return *this;
    }

  protected:
    ~_component_();

  public:
    _component_(const tnt::Compident& ci, const tnt::Urlmapper& um, tnt::Comploader& cl);
    unsigned operator() (tnt::HttpRequest& request, tnt::HttpReply& reply, tnt::QueryParams& qparam);
};

static tnt::EcppComponentFactoryImpl<_component_> Factory("alternativ_hello");

_component_::_component_(const tnt::Compident& ci, const tnt::Urlmapper& um, tnt::Comploader& cl)
  : EcppComponent(ci, um, cl){
}

_component_::~_component_(){}

unsigned _component_::operator() (tnt::HttpRequest& request, tnt::HttpReply& reply, tnt::QueryParams& qparam)
{
    std::vector<std::string> towns;
    towns = {"Hamburg", "Frankfurt", "München"};

    NL::Template::LoaderFile loader;
    NL::Template::Template nl_main_content( loader );
    nl_main_content.load( "src/hello/view/main_content.nlt" );
    nl_main_content.block( "town_items" ).repeat( towns.size() );
    for ( unsigned int i=0; i < towns.size() ; i++ ) {
        nl_main_content.block( "town_items" )[ i ].set( "town_name", towns[ i ] );
    }
    std::stringstream ss_main_content;
    nl_main_content.render( ss_main_content );

    NL::Template::LoaderFile skeletal_loader;
    NL::Template::Template nlTemplate( skeletal_loader );
    nlTemplate.load( "src/hello/view/site_skeletal.nlt" );
    nlTemplate.set( "site_title", "About NLTemplate" );
    nlTemplate.set( "main_content", ss_main_content.str() );

    nlTemplate.render( reply.out() );

    return HTTP_OK;
}

} // namespace

Ich lade hier die zwei Templates-Files:  erst das Template mit dem Haupt-Inhalt, das ich mit generischen Inhalt befülle. Dann lade ich das Template für die Seiten-Struktur und füge den Variablen Teil des Haupt-Inhaltes ein.

NLTemplate bietet auch die Möglichkeit, ein Template in ein anderes zu includieren. Ich mag den Ansatz jedoch nicht, weil dann das Seiten-Skelett-Tamplate in einen Kopf- und Fuß-Teil aufgetrennt werden muss. Wenn das Seiten-Skelett-Tamplate an Komplexität zunimmt, wird es recht schnell unübersichtlich.

Dieses Beispiel funktioniert nur mit dem Master-Branch vom Tntnet-Projekt. In der nächsten Zeit werde ich versuchen das Beispiel auch mit dem 2.Xer-Zweig (dem offiziellen Release-Zweig) zum Laufen zu bekommen.

Advertisements

Ein Gedanke zu “MVC-Architektur in Tntnet (Variante III.)

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s