Tennisclub7 wilde een inschrijfformulier voor nieuwe leden op hun WordPress website. HandiHow heeft een Angular Elements widget gemaakt die kan worden gebruikt op de WordPress site.

Wacht even… Angular Elements? Dit is een nieuw concept waarmee je Angular componenten kunt inbouwen in andere applicaties, die gemaakt zijn met een hele andere technologie. Eigenlijk maak je een klein pakketje javascript dat je op een willekeurige plek op een HTML pagina kunt gebruiken. Zo kun je dit bijvoorbeeld inbouwen in een WordPress pagina!

De code voor dit project is te vinden onder deze link.

Ben je ook geĆÆnteresseerd in een custom widget voor jouw WordPress site? Neem dan contact op met HandiHow. Voor developers staat hieronder het verhaal hoe we dit hebben gemaakt. Let op! Het had nogal wat voeten in de aarde….

Het nieuwe Angular project

Het begon allemaal met de command line . Een aantal dependencies worden toegevoegd om Angular Elements te kunnen gebruiken (de laatste drie statements).

ng new lake7elements
cd lake7elements  
npm i @angular/elements --save 
npm i @webcomponents/custom-elements --save
npm install --save document-register-element

We voegen ook Firebase toe als back-end voor de mini-applicatie. Ook gebruiken we Bootstrap voor de styling van de applicatie.

npm install firebase @angular/fire --save 
npm install --save bootstrap jquery 

We installeren het geweldige SurveyJS om het inschrijfformulier mee te kunnen maken:

npm install survey-angular --save
npm install --save survey-knockout
npm install --save surveyjs-widgets

We maken slechts 1 component, dus dit doen we gewoon in de app.component die automatisch al in het project staat. Belangrijk om hieraan toe te voegen de “encapsulation”.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.Emulated
})

In de app module importeren we “Injector” en “createCustomElement”. Ook voegen we AppComponent toe aan de entryComponents. We voegen een ngDoBootstrap methode toe waarin we het custom element definiĆ«ren.

...
import { NgModule, Injector } from '@angular/core';

import { createCustomElement } from '@angular/elements';
...
entryComponents: [
  	AppComponent
  ]
...
export class AppModule { 
	constructor(private injector: Injector){}

	ngDoBootstrap(){
		
		const el = createCustomElement(AppComponent, { injector: this.injector });

		customElements.define('signup-form', el);
	}
}

In de styles.scss file importeren we de CSS stylesheets van Bootstrap en SurveyJS. Let op! Als je dit probeert te doen in de angular.json file, dan krijgt je component geen styles… Learning curve… dit heeft me veel tijd gekost.

@import 'survey-angular/survey.css';
@import 'bootstrap/dist/css/bootstrap.min.css';

Aan de angular.json file verander je de volgende dingen. Je voegt een javascript file toe aan de scripts. Dit zorgt ervoor dat het element goed wordt geregistreerd. Ook zet je de “outputHashing” op “none”. Dit zorgt ervoor dat de gecompilde output later een voorspelbare filenaam krijgt.

"scripts": [
              "node_modules/jquery/dist/jquery.min.js",
              "node_modules/bootstrap/dist/js/bootstrap.min.js",
              {
                  "input": "node_modules/document-register-element/build/document-register-element.js"
              }
            ]
....
"outputHashing": "none"

Nu kunnen we de component maken. Het formulier maken we met SurveyJS Builder. Kijk in de code van de repo voor meer informatie hoe je SurveyJS kunt inbouwen in Angular. Het is raadzaam om Bootstrap te gebruiken voor de applicatie, want SurveyJS heeft al support voor Bootstrap (er is een thema met Bootstrap). Overigens heeft HandiHow ook al eerder Bulma gebruikt met SurveyJS, dit is geen probleem maar je bent dan langer bezig met het stylen van de formulieren.

Emails versturen

We gaan nu een functie maken die de emails gaat versturen nadat het inschrijfformulier is gemaakt. Dit doen we met Firebase Cloud Functions en SendGrid. In SendGrid maken we eerst twee templates (1 voor degene die zich inschrijft en 1 voor de ledenadministratie).

firebase init
cd functions 
npm install --save @sendgrid/mail
npm install dateformat
firebase functions:config:set sendgrid.key="SENDGRID_API_KEY" sendgrid.id="SENDGRID"

Nu maken we de functie en uploaden deze naar Firebase.

import * as functions from 'firebase-functions';
// import * as admin from 'firebase-admin';
const sgMail = require('@sendgrid/mail');
const dateFormat = require('dateformat');


export const sendEmails = functions.firestore.document('registrations/{id}').onWrite((change, context) => {
	const newValue = change.after.data();
	const previousValue = change.before.data();
	if(newValue && previousValue && newValue.isFinished && !previousValue.isFinished){
		//set the api key for sendgrid
		sgMail.setApiKey(functions.config().sendgrid.key);
		const data = newValue.data;
		const dynamicData = {
			  email: data.email,
	          firstName: data.firstName,
	          lastName: data.lastName,
	          gender: data.gender,
	          birthDate: dateFormat(data.birthDate, "d-m-yyyy"),
	          address: data.address,
	          postalCode: data.postalCode,
	          city: data.city,
	          mobilePhoneNumber: data.mobilePhoneNumber,
	          membershipType: data.membershipType,
	          date: dateFormat(data.date, "d-m-yyyy"),
	          imageUrl: data.image && data.image[0] && data.image[0].content ? data.image[0].content : 'https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_1280.png',
	          signedBy: data.signedBy,
	          placeSigned: data.placeSigned,
	          dateSigned: dateFormat(data.dateSigned, "d-m-yyyy"),
	          currentRatingSingles: data.currentRatingSingles ? data.currentRatingSingles : '-',
	          currentRatingDoubles: data.currentRatingDoubles ? data.currentRatingDoubles : '-'
        };
		const msgToNewMember = {
			// to: data.email,
			to: data.email,
			from: 'no-reply@tennisclub7.nl',
			fromname: 'Ledenadministratie Tennisclub7',
			templateId: 'd-06de23332fd04c43a1258a1c99f090bb',
			dynamic_template_data: dynamicData
		};
		const msgToAdministration = {
			to: 'ledenadministratie@tennisclub7.nl',
			from: 'no-reply@tennisclub7.nl',
			fromname: 'Ledenadministratie Tennisclub7',
			templateId: 'd-08a21deec3c9430ab552406395ed0c61',
			dynamic_template_data: dynamicData
		}
	    return sgMail.send(msgToAdministration)
		    .then(() => {
		    	return sgMail.send(msgToNewMember)
			    .then(() => {
			    	console.log('emails verstuurd');
			    	return true;
			    })
			    .catch((error:any) => {
			    	console.error(error.toString());
					return false;
			    })
		    })
		    .catch((error:any) => {
		    	console.error(error.toString());
				return false;
		    })
	} else {
		console.log('not sending email, the user is not yet finished');
		return false;
	}
	

})

Zoals je kunt zien, checken we eerst of de emails wel verstuurd moeten worden… Want als het formulier tussentijds bewaard wordt, dan moet dit nog niet gebeuren. Daarna maken we de dynamische data voor de templates. Met de handige library “dateFormat” krijgen we de datum gemakkelijk in een Nederlands leesbaar formaat.

De output samenvoegen

Angular maakt normaal gesproken met het “ng build –prod” commando een aantal files. We willen graag maar 1 javascript file, zodat deze gemakkelijk kan worden ingeladen als widget. Hiervoor installeren we fs-extra en concat.

npm install fs-extra concat --save-dev

We maken een nieuwe file “build-elements.js” in de root van het project.

const fs = require('fs-extra');
const concat = require('concat');
(async function build() {
  const files = [
    './dist/lake7elements/runtime-es5.js',
    './dist/lake7elements/polyfills-es5.js',
    './dist/lake7elements/scripts.js',
    './dist/lake7elements/main-es5.js',
    './dist/lake7elements/5-es5.js'
  ]
  await fs.ensureDir('elements')
  await concat(files, 'elements/signup-form.js');
  await fs.copyFile('./dist/lake7elements/styles.css', 'elements/styles.css')

})()

In package.json voegen we een nieuwe regel toe bij de scripts.

"build:elements": "ng build --prod && node build-elements.js"

Nu kunnen we dit commando aanroepen via de command line met:

npm run build:elements  

We maken een index.html file in de folder “elements” die zojuist is gemaakt.

<!DOCTYPE html>
<html>
<head>
	<title>Testing elements</title>
	<script src="signup-form.js"></script>
	<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
	<signup-form></signup-form>
</body>
</html>

We testen dit uit door naar de folder “elements” te gaan en dan http-server commando in te geven en voila! De component werkt!