Load partials via ajax

Hi,
my question is, if it is possible to load partials with data on user interaction. Loading per $get or ajax throws forbidden error. The goal is to reduce DOM size e.g. by loading hidden elements just when they are really needed.
I would be happy about a hint!
Greetings,
Christiana

@christiana83, Interesting question. I did some playing.

Considering:

  • You want to fetch new html dynamically from the server to add to the DOM.
  • The html is generated using Twig with server data.
  • The fetch is triggered by some user interaction.

I did the following:

  • Using fresh Grav 1.7.23

  • Created new partial template /user/themes/quark/templates/partials/newdiv.html.twig with the following content:

    <div id="new-div">{{ message }}</div>
    
  • In /user/themes/quark/templates/default.html.twig I updated the block as
    follows:

    {% block content %}
      <button id="fetch-html">Fetch HTML</button>
      <div id="new-div-parent"></div>
    
      <script>
         const button = document.getElementById('fetch-html');
         button.addEventListener('click', async (event) => {
            try {
               const data = new FormData();
               data.append('fetchHTML', '');
    
               const response = await fetch('/home', {
                  method: 'POST',
                  body: data,
               });
    
               if (response.ok) {
                  const result = await response.json();
                  const html = result.html.trim();
    
                  const template = document.createElement('template');
                  template.innerHTML = html;
    
                  const parent = document.getElementById('new-div-parent');
                  parent.append(template.content.firstChild)
               } else {
                  console.log(response);
               }
            } catch (error) {
              console.log(error);
            }
          })
      </script>
      {{ page.content|raw }}
    {% endblock %}
    
  • Finally I’ve added PHP code to /user/themes/quark/quark.php at the top of event handler onTwigInitialized() to repond to the async fetch:

    public function onTwigInitialized()
    {
        $twig = $this->grav['twig'];
    
        if (isset($_POST['fetchHTML'])) {
            $div = $twig->processTemplate('partials/newdiv.html.twig', 
               ['message' => 'Hello world']
            );
            echo(json_encode(['html' => $div]));
            die();
        }
       
        [...rest of method]
    
  • When browsing to the home page of the site it will look like:
    Untitled

  • After clicking button ‘Fetch HTML’:
    Untitled

Todo:

  • Replace the button with your own smarter “on user interaction”
  • Replace the PHP to load your own “partials with data”
  • And more to polish it…

Hi @pamtbaau,

thank you very much for your quick response and the nice solution.
My question ist, will that work on the static website at the end without php?

@christiana83, The more requirements and context is given in the question the higher the chance are to get a suitable answer…

Back to square one:

partials with data

What are “partials with data”? Are they static? Are they dynamically generated?

Loading per $get or ajax throws forbidden error.

What approach(es) have your tried but “throws forbidden error.”?

My question ist, will that work on the static website at the end without php?

That depends on your environment… If logic is required to create and return “partials with data”, you’ll need a PHP, ASP or a Node based server that can be accessed by the static front-end using Javascript.

@pamtbaau thank you for your answer.

To point a special example, I use the sidebar example.

Partials with data means an theme template partial with twig variables.

/templates/partials/asides.html.twig

The sidebars can be created in admin panel by admin users. The data rendered by twig inside the partial are rendered from a json file.

E.g.

{% if data.asides.sidebars %}
    {% for sidebar in data.asides.sidebars %}
        <aside id="sidebar{{ sidebar.title|hyphenize|camelize }}"
               class="u-sidebar u-unfold--css-animation u-unfold--hidden"
               aria-labelledby="sidebar{{ sidebar.title|hyphenize|camelize }}Invoker">

           {# here comes the content #}

        </aside>
    {% endfor %}
{% endif %}

The Code is rendered at page load and the aside elements are hidden. On click they fold out by JavaScript.

My idea was not just to fold them out by click but also to append the partials content on click to a node, so that they just need to be loaded, when needed.

I tried to load them by JQuery load including the path to the partial file, but that is not allowed (403)

e.g.

if(triggerLink == 'sidebarlink' ){
        someContainer.html('/path/to/partial');
       //[...]
 }

or

if(triggerLink == 'sidebarlink' ){
   $( *selector* ).load('/path/to/partial')
}

The project is in live mode a static website based on static html files delivered by blackhole plugin. JavaScript works on the liveserver. But no php is allowed. That is a requirement of the admins.

@christiana83,

to append the partials content on click to a node, so that they just need to be loaded, when needed

To recap our needs:

  • you want to asynchronously fetch HTML from the server when the user performs a click. That HTML is then turned into a new node and added to the DOM.
  • The HTML being served is generated by a Twig template with data from a Json file

Answer:

  • A web server is required to accept and respond to the async request.
  • Twig is founded by Symfony and is therefor bound to PHP.
  • If nodejs is allowed on the server, a simple HTTP server can be created that:
    • Reads data from the json file
    • Parses the data into a js string template to generate a HTML string
    • Returns the HTML string to the client.
  • If simple js string templates are not sufficient, you could use Twig alternatives available for Nodejs.

Thank you for your answer. I already thought that this would not work directly with twig theme template partial.I wanted to reassure myself that I would not unintentionally take a detour. Then I have to do that directly with Json and Javascript. thank you very much for your time and help