Unusual Combination of Angular and PHP

Posted by Kenneth Lee on 9/1/2017

Introduction

This blog post is inspired by the scenario while working for one of our clients. In this scenario, the HTML were generated dynamically by PHP using WordPress’ contents. It is then received by Angular to parse for the viewers.

Having PHP to handle the HTML code would mean that the Javascript side of Angular has limited interactivity with the template side which takes away the major advantages of Angular 2 of binding events to elements.

With only typescript (ts) portion of component to work with, how do we create user events to the ts functions that generally comes very easy with fully featured Angular?

Surprising (or unsurprising) to say, the solution to this problem came to me a lot easier than I originally thought. However, I still find this to be an interesting situation to warrant a small blog post. So here goes!

Set up

Let’s quickly get the HTML and PHP out of the way. Here’s a short example of how PHP dynamically generate the HTML code.

<?php $data = array("foo", "bar", "foobar"); for($i = 0; $i < count($data); $i++) { ?> <pre style="margin: 5px auto; width: 90%;" class="pre__clickable"> <?php echo $data[$i] ?> </pre> 
<?php } ?>

 

The output of PHP code below is then converted to string…

<pre style="margin: 5px auto; width: 90%;" class="pre__clickable"> foo </pre>
<pre style="margin: 5px auto; width: 90%;" class="pre__clickable"> bar</pre>
<pre style="margin: 5px auto; width: 90%;" class="pre__clickable"> foobar </pre>

…and is parsed by Angular 2’s innerHTML directive’:

<!-- app.component.html -->

  <!-- Stringified HTML Code is passed as'content' -->
<div [innerHTML]="content" >  </div>

The following view is the result.

Solution

Angular 2 has a feature called ElementRef in the Angular/Core library which returns the HTML elements in the HTML template. You can find the element you are looking for when the ElementRef is used within Angular 2’s lifecycle event ngAfterViewInit().

/* app.component.ts */

import { Component, AfterViewInit, ElementRef } from '@angular/core';

. . . . .

. . . . .

constructor( private container: ElementRef ) {}

ngAfterViewInit() {
   /*
    this.container is the reference to the entire HTML collection in app.component.html
   */
}

…and that’s it! You now have direct access to all HTML elements inside the component!

Now lets dynamically bind the event listener to the HTML elements. Because Angular 2’s core feature of event binding is out of the equation due to templates generated by PHP, we must employ Vanilla JavaScript to get the job done.

In this exercise, let’s bind click event to each <pre> elements to toggle the following CSS class:

.pre__highlight {
    background: antiquewhite;
}

The easiest way to isolate the elements you need is to use the following command inside the ngAfterViewInit():

this.container.nativeElement.querySelectorAll(‘.pre__clickable’);

… which returns Node List (length = 3) of pre.pre__clickable.

The next step is the iterate through the HTML Node and dynamically bind the function to be invoked when clicked. Below is the full overview of the dynamic event binding:

//ngAfterViewInit(): invoked once after the HTML elements are rendered
ngAfterViewInit() {
    const preElements = this.container.nativeElement.querySelectorAll('.pre__clickable');

//Iterate all the <pre> elements available to add click event.
    for(let i = 0; i < preElements.length; i++) {
        let pre = preElements[i];
        pre.addEventListener('click', this.onClick.bind(this, preElements[i]
    }
}

//This Typescript function is bound to the <pre> element
onClick(element, event) {
    event.preventDefault();
    const answer = element.classList.toggle('pre__highlight');    
}

After retrieving the NodeList, the NodeList is iterated using for loop. For every iteration, click-event-listener is attached to the <pre> element which invokes the this.onClick that is defined inside the app.component.ts

Below is the final result:

If you ever find yourself in a bizarre situation of mixing Angular 2 with PHP template system, know that not all is lost. I doubt many will ever find themselves in this situation but I hope you fond this work around at least interesting. For me, it brought a renewed appreciation of how Angular really makes event-binding much easier.

Thank you for reading! Please be sure to leave a comment or questions below. Constructive feedback are always welcome!