I have a class called HtmlFile
which has a method called generate
. This method is responsible for generating an html file from handlebars template and data object provided in the HtmlFile
class constructor.
I am confused on what code design should I use for this use case and what variable names to choose.
class HtmlFile {
public file: any = null;
private template: any = null;
private data: any = null;
constructor(templatePath: string, params: any) {
this.template = path.resolve(templatePath);
this.data = params;
}
public async generate() {
try {
const error = this._validate();
if (error) {
throw error;
}
const fileContent = fs.readFileSync(this.template, "utf8");
const html = handlebars.compile(fileContent)(this.data);
return html;
} catch (e) {
throw e;
}
}
private _validate() {
let errStr: any = null;
if (typeof this.template !== "string") {
errStr = `expected template path to be a string, instead got
${typeof this.template}`;
}
if (typeof this.data !== "object") {
errStr = `expected data to be an object, instead got ${typeof
this.data}`;
}
return errStr;
}
}
And I am using this class in the following manner:
let htmlFile = new HtmlFile("./src/templates/sample.hbs", invoiceOptions);
const html: any = await htmlFile.generate();
return html;
I also want suggestions on what variable names I should use in place of htmlFile
and html
.
1 Answer 1
First things first - syntax:
Why is generate an async function? You should change the way the files are read. Please read the answers to this question: https://stackoverflow.com/questions/46867517/how-to-read-file-with-async-await-properly
Logic:
The _validate
function checks integrity of your object. All the data it needs is available in the constructor and this is where this function should be called. Never defer throwing an exception if you cannot avoid it.
Structure:
As for what design to use - your class is doing multiple things at the same time. Reading a template file from disk is one thing - and it requires handling an asynchronous operation. Then there is template compilation - which in case of HBS is not asynchronous and there is rendering - not asynchronous again. I'd recommend to restructure the solution a bit:
- Use one class to read a file and return you a compiled template object. In case you needed to reuse your template objects - do not read from disk on every template call.
- The returned class (let's call this one
HandlebarsTemplate
) should take a string as its constructor input and attempt compiling it as HBS. If the template was not correct - you get an exception thrown on object creation. - Note that
HandlebarsTemplate
could be reused for templates coming from sources other than files e.g. database or just a string literal in the code. - The object
HandlebarsTemplate
should expose a method calledgenerate(data)
. This method should attempt renderingdata
the with the compiled HBS template. - This way you get reusable template objects
This would be up to you if you want to read the template each time or store a compiled instance in memory. Both approaches have their pros and cons.