1
\$\begingroup\$

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.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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 called generate(data). This method should attempt rendering data 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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.