Snippets in Templates

Added by Torsten Schulz 10 months ago

Hi,

I'll use in Templates Snippets that I use then in many other templates. Like

Page 1
<snippet menu>
<div>
Page content
</div>

Page 2
<snippet menu>
<span>
Other content
</span>

How can I do that with Witty?


Replies (8)

RE: Snippets in Templates - Added by Marcelo Antunes 10 months ago

First put the repeated snippet inside a message tag, on the template file.
Then, on your template (page 1 and page 2) replace the part that you need to be always the same by a placeholder, and on witty bind it as a template widget.
With that you can then get the repeated code from the template file.

For example:
on themplates xml file:

<messages>
     <message id="menutemp">
          some xhtml code for menu
     </message>
     <message id="page1">
          ${menu}
          other xhtml code for page 1
     </message>
     <message id="page2">
          ${menu}
          other xhtml code for page 2
     </message>
</messages>

on witty:
//binding the template of page 1
 auto t = std::make_unique<Wt::WTemplate>(Wt::WString::tr("page1"));
//binding the template fom menu
auto menuTemp= t->bindWidget("menu",std::make_unique<Wt::WTemplate>(Wt::WString::tr("menutemp")));

//binding other widgets tat you need

//add widgets to root container
 root().addWidget(std::move(t));

RE: Snippets in Templates - Added by Wim Dumon 9 months ago

There's also the possibility to use template functions to achieve this. Two specific ones are of interest:

  ${tr:menu-snippet}
  ${block:menu-snippet p1 p2}

${tr:key} is an in-place equivalent of template->bindString("xxx", tr("key"));

block is similar to tr, but can have arguments. See documentation of WTemplate::Functions.

RE: Snippets in Templates - Added by Torsten Schulz 9 months ago

Ok, I got it working, thank you Marcelo. But I wish there would be an easier way like

{include:<filename>}

I tried to do it by myself, but even that it is working in general, there is a problem: After including my ${fieldname} parts get parsed, so that I get an fieldname and the bindWidget isn't working. Maybe it is possible to add that functionality in one of the future releases? I think it is a standard functionality of templates this days.

RE: Snippets in Templates - Added by Torsten Schulz 9 months ago

Ok, forget the question. I forgot that WTemplate is using strings instead of files. Sorry for that.

RE: Snippets in Templates - Added by Torsten Schulz 9 months ago

For the ones who also like a WTemplate with files instead of just strings, I created this patch.
It also has an include:

${include:<filename>}

And a constructor which based on filename instead of a string (of course).
And both, WTemplate and WFileTemplate have a new constructor which includes the Textformat
WTemplate(const WString& text, TextFormat textFormat)

diff -ruN wt-4.1.1/src/CMakeLists.txt wt-4.1.1.patched/src/CMakeLists.txt
--- wt-4.1.1/src/CMakeLists.txt    2019-09-09 12:00:10.000000000 +0200
+++ wt-4.1.1.patched/src/CMakeLists.txt    2019-12-24 12:09:08.890713226 +0100
@@ -119,6 +119,7 @@
 Wt/WException.h Wt/WException.C
 Wt/WFileDropWidget.h Wt/WFileDropWidget.C
 Wt/WFileResource.h Wt/WFileResource.C
+Wt/WFileTemplate.h Wt/WFileTemplate.C
 Wt/WFileUpload.h Wt/WFileUpload.C
 Wt/WFitLayout.h Wt/WFitLayout.C
 Wt/WFlashObject.h Wt/WFlashObject.C
diff -ruN wt-4.1.1/src/Wt/WFileTemplate.C wt-4.1.1.patched/src/Wt/WFileTemplate.C
--- wt-4.1.1/src/Wt/WFileTemplate.C    1970-01-01 01:00:00.000000000 +0100
+++ wt-4.1.1.patched/src/Wt/WFileTemplate.C    2019-12-24 15:14:11.479045913 +0100
@@ -0,0 +1,57 @@
+#include "WFileTemplate.h" 
+
+#include <boost/algorithm/string.hpp>
+#include <string>
+#include <iostream>
+#include <cctype>
+#include <fstream>
+#include <streambuf>
+#include <exception>
+
+#include "Wt/WApplication.h" 
+#include "Wt/WContainerWidget.h" 
+#include "Wt/WLogger.h" 
+
+#include "EscapeOStream.h" 
+#include "WebUtils.h" 
+#include "DomElement.h" 
+#include "RefEncoder.h" 
+#include "WebSession.h" 
+
+namespace Wt {
+LOGGER("WFileTemplate");
+
+WString &WFileTemplate::_include(WString& text)
+{
+  auto original = text.toUTF8();
+  for (auto pos = original.find("${include:"); pos != std::string::npos; pos = original.find("${include:", pos)) {
+    auto end = original.find("}", pos + 1);
+    auto fileName = original.substr(pos + 10, end - pos - 10);
+    std::ifstream file(fileName, std::ifstream::in);
+    std::string include((std::istreambuf_iterator<char>(file)),
+                    std::istreambuf_iterator<char>());${include:<filename>}
+    original.replace(pos, end - pos + 1, include);
+  }
+  text = WString::fromUTF8(original);
+  return text;
+}
+
+WFileTemplate::WFileTemplate(const WString& fileName) 
+{
+  std::ifstream file(fileName.toUTF8(), std::ifstream::in);
+  std::string templateText((std::istreambuf_iterator<char>(file)),
+  std::istreambuf_iterator<char>());
+  plainTextNewLineEscStream_ = new EscapeOStream();
+  plainTextNewLineEscStream_->pushEscape(EscapeOStream::PlainTextNewLines);
+  setInline(false);
+  Wt::WString test = WString::fromUTF8(templateText);
+  setTemplateText(test, TextFormat::XHTML);
+}
+
+void WFileTemplate::setTemplateText(WString& text, TextFormat textFormat)
+{
+  auto zw = _include(text);
+  WTemplate::setTemplateText(zw, textFormat);
+}
+
+}
diff -ruN wt-4.1.1/src/Wt/WFileTemplate.h wt-4.1.1.patched/src/Wt/WFileTemplate.h
--- wt-4.1.1/src/Wt/WFileTemplate.h    1970-01-01 01:00:00.000000000 +0100
+++ wt-4.1.1.patched/src/Wt/WFileTemplate.h    2019-12-24 14:18:58.852074722 +0100
@@ -0,0 +1,29 @@
+// This may look like C code, but it's really -*- C++ -*-
+/*
+ * Copyright (C) 2009 Emweb bvba, Kessel-Lo, Belgium.
+ *
+ * See the LICENSE file for terms of use.
+ */
+#ifndef WFILETEMPLATE_H_
+#define WFILETEMPLATE_H_
+
+#include <Wt/WTemplate.h>
+#include <Wt/WString.h>
+
+namespace Wt {
+
+class WT_API WFileTemplate : public WTemplate
+{
+public:
+  WFileTemplate(const WString& fileName);
+    
+ 
+  void setTemplateText(WString& text, TextFormat textFormat);
+  
+private:${include:<filename>}
+  WString &_include(Wt::WString& text);
+
+};
+
+}
+#endif
diff -ruN wt-4.1.1/src/Wt/WTemplate.C wt-4.1.1.patched/src/Wt/WTemplate.C
--- wt-4.1.1/src/Wt/WTemplate.C    2019-09-09 12:00:10.000000000 +0200
+++ wt-4.1.1.patched/src/Wt/WTemplate.C    2019-12-24 12:04:11.852944490 +0100
@@ -158,12 +158,6 @@
 #endif

 WTemplate::WTemplate()
-  : previouslyRendered_(nullptr),
-    newlyRendered_(nullptr),
-    encodeInternalPaths_(false),
-    encodeTemplateText_(true),
-    changed_(false),
-    widgetIdMode_(TemplateWidgetIdMode::None)
 {
   plainTextNewLineEscStream_ = new EscapeOStream();
   plainTextNewLineEscStream_->pushEscape(EscapeOStream::PlainTextNewLines);
@@ -172,6 +166,14 @@
 }

 WTemplate::WTemplate(const WString& text)
+{
+  plainTextNewLineEscStream_ = new EscapeOStream();
+  plainTextNewLineEscStream_->pushEscape(EscapeOStream::PlainTextNewLines);
+  setInline(false);
+  setTemplateText(text);
+}
+
+WTemplate::WTemplate(const WString& text, TextFormat textFormat)
   : previouslyRendered_(nullptr),
     newlyRendered_(nullptr),
     encodeInternalPaths_(false),
@@ -182,7 +184,7 @@
   plainTextNewLineEscStream_ = new EscapeOStream();
   plainTextNewLineEscStream_->pushEscape(EscapeOStream::PlainTextNewLines);
   setInline(false);
-  setTemplateText(text);
+  setTemplateText(text, textFormat);
 }

 WTemplate::~WTemplate()
diff -ruN wt-4.1.1/src/Wt/WTemplate.h wt-4.1.1.patched/src/Wt/WTemplate.h
--- wt-4.1.1/src/Wt/WTemplate.h    2019-09-09 12:00:10.000000000 +0200
+++ wt-4.1.1.patched/src/Wt/WTemplate.h    2019-12-24 13:43:40.832294739 +0100
@@ -371,6 +371,13 @@
    */
   WTemplate(const WString& text);

+  /*! \brief Creates a template with given template.
+   *
+   * The \p templateText type can be set as wished.
+   * See Wt::TextFormat.
+   */
+  WTemplate(const WString& text, TextFormat textFormat);
+
   virtual ~WTemplate();

   /*! \brief Returns the template.
@@ -823,33 +830,35 @@
    */
   void reset();

+protected:
+  WString text_;
+  EscapeOStream* plainTextNewLineEscStream_;
 private:
   typedef std::map<std::string, Function> FunctionMap;
   typedef std::map<std::string, WString> StringMap;
   typedef std::map<std::string, std::unique_ptr<WWidget> > WidgetMap;
   typedef std::set<std::string> ConditionSet;

-  std::set<WWidget *> *previouslyRendered_;
-  std::vector<WWidget *> *newlyRendered_;
+  std::set<WWidget *> *previouslyRendered_ = nullptr;
+  std::vector<WWidget *> *newlyRendered_ = nullptr;

   FunctionMap functions_;
   StringMap strings_;
   WidgetMap widgets_;
   ConditionSet conditions_;

-  WString text_;
   std::string errorText_;

-  bool encodeInternalPaths_, encodeTemplateText_, changed_;
-  TemplateWidgetIdMode widgetIdMode_;
+  bool encodeInternalPaths_ = false;
+  bool encodeTemplateText_ = true;
+  bool changed_ = false;
+  TemplateWidgetIdMode widgetIdMode_ = TemplateWidgetIdMode::None;

   std::string encode(const std::string& text) const;
   static std::size_t parseArgs(const std::string& text,
                    std::size_t pos,
                    std::vector<WString>& result);
   void unrenderWidget(WWidget *w, DomElement &el);
-
-  EscapeOStream* plainTextNewLineEscStream_;
 };

 template <typename T> T WTemplate::resolve(const std::string& varName)

RE: Snippets in Templates - Added by Roel Standaert 9 months ago

If you want to extend WTemplate with the ability to include files, I think it's a better idea to look at addFunction to just add a function that does your include functionality. You can use renderTemplateText in its implementation, just like we implemented the block function.

Your approach is possible, too, if you want to avoid rereading the files on every rerender of the template (another possibility would be to just cache the result of your include function). I don't see why you would need to hack WTemplate to make that work, though. As far as I can tell, all you need is a little utility function that does your substitutions before it sets the template text.

RE: Snippets in Templates - Added by Torsten Schulz 9 months ago

Hi Roel,

thank you for your answer. My first intention really was just to make the include functionality. But for me the file based Template functionality was a feature that I was missing. Also I thought it isn't needed to request you to add it, because I think you'd think different: We could use the WString::tr functionality, what's not wrong. But maybe other people also prefer the file based template instead of the string based, and so I thought I created the new class. But for that it was needed to be able to access some values in the new class, what wasn't possible. And to set the values in the header instead of the constructor is recommended by many C++ coaches (Reinhard Grimm for example).

That are the reasons that I implemented that new class, even that it was possible to do the include in another way.

Best regards and a happy new year.
Torsten Schulz

RE: Snippets in Templates - Added by Roel Standaert 9 months ago

Happy new year to you too.

I was mostly just warning you that there may be other ways to do it, while changing WTemplate is not very maintainable.

(1-8/8)