1

I'm starting with cgicc which let me keep code short and clean. But now I'm struggling. I want to have something like that:

<ul>
  <li>
    <a href="#" ><span>Main 1</span></a>
    <ul>
      <li><a href="ref1-1" ><span>Sub 1</span></a></li>
      <li><a href="ref1-2" ><span>Sub 2</span></a></li>
      <li><a href="ref1-3" ><span>Sub 3</span></a></li>
    </ul>
  </li>
  <li>
    <a href="#" ><span>Main 2</span></a>
    <ul>
      <li><a href="ref2-1" ><span>Sub 1</span></a></li>
      <li><a href="ref2-2" ><span>Sub 2</span></a></li>
      <li><a href="ref2-3" ><span>Sub 3</span></a></li>
    </ul>
  </li>
</ul>

The inner blocks are very large, therefore I don't want to combine the calls. My first atttempt like this (I hope I coded it well):

cout << ul() << endl;
  cout << li() << endl;
    cout << a("Main 1").set("href", "#") << endl;
    cout << ul() << endl; // <-- must fail here!
      cout << li(a("Sub 1").set("href", "ref1-1")) << endl;
      cout << li(a("Sub 2").set("href", "ref1-2")) << endl;
      cout << li(a("Sub 3").set("href", "ref1-3")) << endl;
    cout << ul() << endl;
  cout << li() << endl;
  cout << li() << endl;
    cout << a("Main 2").set("href", "#") << endl;
    cout << ul() << endl;
      cout << li(a("Sub 1").set("href", "ref2-1")) << endl;
      cout << li(a("Sub 2").set("href", "ref2-2")) << endl;
      cout << li(a("Sub 3").set("href", "ref2-3")) << endl;
    cout << ul() << endl;
  cout << li() << endl;
cout << ul() << endl;

Problem is the boolean state for elements like < ul > and < li > etc. So, is there a best practice and smart solution to handle this? - Andi

EDIT: My new solution: additional elements class "simple":

#include <cgicc/HTMLElement.h>
#include <cgicc/HTMLAttributeList.h>

template<class Tag>
class HTMLSimpleElement : public cgicc::HTMLElement
{
public:
  HTMLSimpleElement() : HTMLElement(0, 0, 0, EElementType(-1))
  {}

  HTMLSimpleElement(const cgicc::HTMLAttributeList& attributes) : HTMLElement(&attributes, 0, 0, EElementType(-1))
  {}

  virtual ~HTMLSimpleElement() {}

  virtual inline HTMLElement* clone() const
  { return new HTMLSimpleElement<Tag>(*this); }

  virtual inline const char* getName() const
  { return Tag::getName(); }

  virtual void render(std::ostream& out) const
  {
    const cgicc::HTMLAttributeList* attributes = getAttributes();

    out << '<' << getName();

    if (attributes != NULL)
    {
      out << ' ';
      attributes->render(out);
    }

    out << ">";
  }
};

#define TAG(name, tag) \
class name##Tag   \
{ public: inline static const char* getName() { return tag; } }

#define SIMPLE_ELEMENT(name, tag) \
TAG(name, tag); typedef HTMLSimpleElement<name##Tag> name

SIMPLE_ELEMENT (divB, "div");
SIMPLE_ELEMENT (divE, "/div");

// and so on... 

This way I can use the member functions and have full control over the BEGIN and END tag. Anybody another, smarter solution?

Dag
  • 10,079
  • 8
  • 51
  • 74
Andi
  • 888
  • 2
  • 10
  • 24
  • I can offer a workaround doing this: defining my own BOOLEAN_ELEMENTs like: BOOLEAN_ELEMENT(ul1, "ul"); BOOLEAN_ELEMENT(ul2, "ul"); and so on. Then I use every specific numbered on the associated level. Any other ideas? – Andi Nov 01 '11 at 15:27

1 Answers1

2

This is kind of ancient, and you've probably found your answer but it could help somebody if they have the same issue.

Your second code block is incorrect. When you cout a tag with cgicc, it outputs the entire tag, including encompassed children. Without encompassed children, the state is memorized and swapped with each construction. This means that to stack them, you should probably use objects that you assemble as a tree. For a simplified example, take this shortened desired output:

<ul>
  <li>
    <a href="#" ><span>Main 1</span></a>
    <ul>
      <li><a href="ref1-1" ><span>Sub 1</span></a></li>
      <li><a href="ref1-2" ><span>Sub 2</span></a></li>
      <li><a href="ref1-3" ><span>Sub 3</span></a></li>
    </ul>
  </li>
</ul>

If you try the cgicc code as such:

cout << ul() << endl;
  cout << li() << endl;
    cout << a("Main 1").set("href", "#") << endl;
    cout << ul() << endl; // <-- must fail here!
      cout << li(a("Sub 1").set("href", "ref1-1")) << endl;
      cout << li(a("Sub 2").set("href", "ref1-2")) << endl;
      cout << li(a("Sub 3").set("href", "ref1-3")) << endl;
    cout << ul() << endl;
  cout << li() << endl;
cout << ul() << endl;

You will get this mess, which is not correct html:

<ul>
<li>
<a href="#" >Main 1</a>
</ul>
<li><a href="ref1-1" >Sub 1</a></li>
<li><a href="ref1-2" >Sub 2</a></li>
<li><a href="ref1-3" >Sub 3</a></li>
<ul>
</li>
</ul>

You should probably be handling this in a more object-oriented manner, keeping track of the objects you use to build your page, like so (pointers will keep track of newly added children, and will avoid making unnecessary copies; without pointers, you need to be sure to add tags in the correct order):

ul* outerList = new ul();
li* outerItem = new li();
ul* innerList = new ul();

outerList->add(outerItem);
outerItem->add(a(span("Main 1")).set("href", "#"));
outerItem->add(innerList);
innerList->add(li(a(span("Sub 1")).set("href", "ref1-1")));
innerList->add(li(a(span("Sub 2")).set("href", "ref1-2")));
innerList->add(li(a(span("Sub 3")).set("href", "ref1-3")));

cout << *outerList << endl;

You can also build them on the fly, using constructors and add and set methods:

cout << ul(li().add(a(span("Main 1")).set("href", "#")).add(ul().add(li(a(span("Sub 1")).set("href", "ref1-1"))).add(li(a(span("Sub 2")).set("href", "ref1-2"))).add(li(a(span("Sub 3")).set("href", "ref1-3"))))) << endl;

This is arguably less clear, harder to follow, harder to debug, and easier to make mistakes; you may be able to alleviate it with some line breaks and indentation, but I don't know how much that will help.

Think of the HTML as a hierarchy. I always had a better time simply using XHTML with libxml.

Taywee
  • 1,313
  • 11
  • 17
  • Thanks for pointing out the tree behavior. Indeed, I didn't realize this possibility. I am not sure if refactoring my code to this kind of from-inner-to-outer definition would be more readable, but it would be the correct way for this library. The last example using a monster-line is definitly not recommended ;-) – Andi Apr 07 '13 at 16:37