Salut Je m'appelle Roman et je ne suis pas l'inventeur des vélos. J'aime le framework Angular et l'écosystème qui l'entoure, et je développe mes applications web avec. De mon point de vue, le principal avantage d'Angular à long terme est basé sur la séparation du code entre HTML et TypeScript, qui a été décrit en détail par l'un de ses développeurs
pourquoi-angular-renders-components-with.html Cet avantage a un inconvénient: le besoin de compilation en principe et la complexité de la compilation dynamique des composants lors de l'exécution. Et donc je veux utiliser la syntaxe de modèle angulaire familière pour donner à l'utilisateur leurs applications la possibilité de personnaliser les modèles de lettres, de générer des rapports et des feuilles de calcul pour l'impression, ou de définir le format d'exportation pour les fichiers xml! Pour savoir comment faire, bienvenue chez cat!
Défi
En général, l'utilisation de modèles angulaires par l'utilisateur peut ressembler à ceci: nous avons un certain ensemble de données:
const data = { project: 'MySuperProject', userName: 'Roman', role: 'admin', projectLink: 'https://example.com/my-super-projectproject' }
Il est nécessaire de donner la possibilité de personnaliser le texte de la lettre, qui sera envoyé à l'utilisateur après l'édition du projet. En utilisant un modèle angulaire, cela pourrait ressembler à ceci:
<body> ! {{project}} <a href="{{projectLink}}">3D </a> <div *ngIf="role == 'admin'"> <a href="{{projectLink}}?mode=edit"></a> </div> </body>
Bibliothèque de modèles Ng
Ce problème peut être résolu en utilisant le compilateur angulaire côté client (ou même côté serveur), mais il prend beaucoup de temps et nécessitera de faire glisser beaucoup de mégaoctets de code vers le client. Pourquoi le compilateur angulaire est-il si gros? Cela est dû au fait qu'il prend en charge une mer de fonctionnalités diverses pour la composition de composants et de modules, et contient également son propre analyseur HTML! J'ai donc décidé d'écrire un convertisseur de modèle angulaire minimal qui utilisera l'analyseur HTML intégré au navigateur. Nous avons réussi à le faire en seulement 200 avec quelques lignes de code en quelques heures. J'ai décidé de partager le résultat avec le public sur
GitHubL'utilisation de la bibliothèque ng-template est assez simple:
Installer la dépendance depuis npm
npm install --save @quanterion/ng-template
ou à travers du fil
yarn add @quanterion/ng-template
Et utilisez-le comme suit:
import { compileTemplate, htmlToElement } from '@quanterion/ng-template'; async test() { let data = { name: 'Roman' }; let element = htmlToElement(`<div>{{name}}</div>`); await compileTemplate(element, data); alert(element.outerHTML); }
Syntaxe prise en charge
- Expressions {{expression}} avec la possibilité d'accéder aux variables et aux fonctions d'appel
- Ng-templates
- Ng-container
- Conditions * ngIf + * ngIf as
- Cycles * ngPour
- Styles [style.xxx] = "valeur" et [style.xxx.px] = "valeur"
- Classes conditionnelles [class.xxx] = "valeur"
- Observables {{name $}} avec abonnement automatique à une valeur (en tant que canal asynchrone)
Voir
les tests
ng-template.spec.ts pour plus de détails.
Utiliser Eval
Pour évaluer les expressions dans les modèles, eval est utilisé avec préférence et courtisanes. Le fait est que dans les modèles angulaires, l'accès aux variables est utilisé sans le préfixe JavaScript habituel. Par conséquent, vous devez appeler eval (), qui a toutes les variables de l'objet de données dans la portée. Je n'ai pas réussi à générer un tel code pour eval (), car voir le code
const data = { a: 1, b: () => 4 }; const expression = 'a+b()'; eval('a =1; b = ??;' + expression);
ne permet pas de passer des fonctions
La solution a été trouvée en créant une fonction dont les paramètres ont les noms de champs de l'objet avec des données:
const data = { a: 1, b: () => 4 }; let entries = [] for (let property in data ) { entries.push([property, data[property]]) } const params = entries.map(e => e[0]); const fun = new Function('code', ...params, `return eval(code)`); const args = entries.map(e => e[1]); const expression = 'a+b()'; const result = fun.call(undefined, expression , ...args);
PS: J'espère qu'à l'avenir, lorsque l'API du nouveau compilateur Ivy se stabilisera, il sera possible de générer un ensemble d'opérateurs pour Ivy et de créer des composants à part entière en dynamique!
Lien vers la source