Introduction
This is an npm plugin to help you generate angular2 components from HTML files.
Background
Angular2 is a component based front-end JavaScript framework. It's easy to write a "@Component
" which includes "template
" in the notation but not easy to split an existing html into components. In fact, a lot of front-end development starts from the html layout. After the html layout is approved by the client, programmers will start to split the html code into several "blocks" - I mean "@components
" in Angular2.
Given an html file includes two selectors "mytag
" and "mytag1
" which looks like below:
<mytag>
<mytag1><div>Div Test</div></mytag1>
</mytag>
In fact, with the html code above, you can easily write the component skeleton because there are only two components in the html.
@Component({
selector:'mytag1',
template:`
<div>Div Test</div>
`,
directives: [],
styles: []
})
export class Mytag1Component {
}
import { Component } from '@angular/core';
import { Mytag1Component } from '/mytag/mytag1';
@Component({
selector:'mytag',
template:`
<mytag1><div>Div Test</div></mytag1>
`,
directives: [mytag1],
styles: []
})
export class MytagComponent {
}
But if you have over 10 components in an html file, can you write all the code patiently?
This tool can help you generate the angular2 component skeletons easily from html mockup.
Source Code
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var util = require('util');
var camel = require('to-camel-case');
var capital = require('to-capital-case');
var jsdom = require("jsdom");
const ANGULAR_IMPORT = `import { Component } from '@angular/core';
%s`;
const ANGULAR_COMPONENT = `@Component({
selector:'%s',
template:\`
%s
\`,
directives: [%s],
styles: []
})`;
const ANGULAR_CLASS = `export class %s {
}
`;
module.exports = {
generate: function (sourceDir, destDir, rootTag) {
console.log("sourceDir: " + sourceDir);
console.log("destDir: " + destDir);
console.log("rootTag: " + rootTag);
var done = function (para, files) {
if (files === null || files === undefined) { return }
for (var i = 0; i < files.length; i++) {
if (files[i].indexOf('.htm') < 0) {
continue;
}
fs.readFile(files[i], 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
var document = jsdom.jsdom(data);
function getNodeType(node) {
if (node === null || node === undefined) return "[object Null]";
return node.toString();
}
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var tags = [];
var directives = [];
var components = [];
var htmltags = [];
var getSubComponentsImport = function (childParents, subComponents) {
var component = "";
for (var j = 0; j < subComponents.length; j++) {
component += "import { " + capital(camel(subComponents[j])) +
"Component } from '" + childParents[0].toLowerCase() + "/" +
subComponents[j] + "';\r\n";
}
return component;
}
var visit = function (node, parentTagPath, parentNodeName) {
if (node === null || node === undefined || node.tagName === undefined) {
return;
}
parentTagPath = parentTagPath.toLowerCase();
parentNodeName = parentNodeName.toLowerCase();
if ("[object HTMLUnknownElement]" === getNodeType(node)) {
if (tags.indexOf(parentTagPath + "/" + node.tagName.toLowerCase()) < 0) {
tags.push(parentTagPath + "/" + node.tagName.toLowerCase());
directives.push(node.tagName.toLowerCase());
var abosolutePath = path.join(destDir,
(parentTagPath + "/" + node.tagName).toLowerCase());
if (!fs.existsSync(abosolutePath)) {
mkdirp(abosolutePath);
}
}
}
var subComponents = [];
var templates = [];
var childParents = [];
var childParentNames = [];
for (var j = 0; j < node.childNodes.length; j++) {
var childsParentTagPath = parentTagPath;
var childsParentNodeName = parentNodeName;
if ("[object HTMLUnknownElement]" === getNodeType(node)) {
childsParentTagPath += "/" + node.tagName.toLowerCase();
childsParentNodeName = node.tagName.toLowerCase();
}
visit(node.childNodes[j], childsParentTagPath, childsParentNodeName);
if ("[object HTMLUnknownElement]" === getNodeType(node.childNodes[j])) {
if (subComponents.indexOf
(node.childNodes[j].tagName.toLowerCase()) < 0) {
subComponents.push(node.childNodes[j].tagName.toLowerCase());
}
}
if (childsParentTagPath != undefined) {
childParents.push(childsParentTagPath);
childParentNames.push(childsParentNodeName);
templates.push(node.innerHTML);
}
}
if (subComponents.length > 0) {
if (childParentNames[0] != "") {
var importComponents = util.format(ANGULAR_IMPORT,
getSubComponentsImport(childParents, subComponents));
var directive = getSubComponentsImport(childParents, subComponents);
var template = util.format(ANGULAR_COMPONENT
, childParentNames[0].toLowerCase()
, templates[0]
, subComponents.join(","));
var exportClass = util.format(ANGULAR_CLASS
, capital(camel(childParentNames[0])) + "Component");
componentPath = childParents[0];
var abosolutePath = path.join(destDir, componentPath);
if (!fs.existsSync(abosolutePath)) {
mkdirp(abosolutePath);
}
fs.writeFile(path.join(destDir, componentPath,
capital(camel(childParentNames[0])) +
"Component" + ".ts"), importComponents + template +
exportClass, function (err) {
if (err) return console.log(err);
});
console.log(importComponents);
console.log(template);
console.log(exportClass);
}
} else {
if ("[object HTMLUnknownElement]" === getNodeType(node) &&
childParentNames[0] != undefined) {
var template = util.format(ANGULAR_COMPONENT
, childParentNames[0].toLowerCase()
, templates[0]
, "");
var exportClass = util.format(ANGULAR_CLASS
, capital(camel(node.tagName)) + "Component");
componentPath = childParents[0];
var abosolutePath = path.join(destDir, componentPath);
if (!fs.existsSync(abosolutePath)) {
mkdirp(abosolutePath);
}
fs.writeFile(path.join(destDir, componentPath,
capital(camel(node.tagName)) + "Component" + ".ts"),
template + exportClass, function (err) {
if (err) return console.log(err);
});
console.log(template);
console.log(exportClass);
}
}
}
visit(document.childNodes[0], "", "");
});
}
}
var walk = function (dir, done) {
console.log(dir);
var results = [];
fs.readdir(dir, function (err, list) {
if (err) return done(err);
var i = 0;
(function next() {
var file = list[i++];
if (!file) return done(null, results);
file = dir + '/' + file;
fs.stat(file, function (err, stat) {
if (stat && stat.isDirectory()) {
walk(file, function (err, res) {
results = results.concat(res);
next();
});
} else {
results.push(file);
next();
}
});
})();
});
};
console.log('begin to generate angular2 components. current output directory : ' + destDir);
walk(sourceDir, done);
}
};
Using the Code
Download the source code from "https://github.com/jasondu168/ngen".
Installation
npm install ngen --save
Usage
Two parameters are needed to run the plug in.
SourceDir
- The directory contains html files DestDir
- The directory for generated TS files
ngen SourceDir DestDir
Use directly from JavaScript.
var ng = generate("/htmls", "/angular2", "");
History