Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
337 views
in Technique[技术] by (71.8m points)

handlebars.js - How to combine Handlebars with Apache Cordova?

Apache Cordova deals poorly with html, since native html does not allow partial files nor templates. I want to use standard Handlebars because it allowed me to structure my html, but still having a full complete single html file produced after cordova prepare and before cordova compile. That is, contrary to jQuery, the html partial files should not be loaded in runtime, but assembled before cordova compile. I do not want to use handlebar templates in the browser either for the same reason. I want everything to be assembled before being compiled.

How then can I combine Handlebars with Apache Cordova, all the files being assembled into html before being deployed/compiled?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I solved this problem with a hook. Here's my solution:

Structure

The html/handlebars file structure will be something like this inside your www dir:

.
+-- index.hbs
+-- html-partials
    +-- head.hbs
    +-- partial1.hbs
    +-- partial2.hbs
    +-- footer.hbs

Your index.hbs may look like:

<!DOCTYPE html>
<html>
    <head>
        {{> head}}
    </head>
    <body>
        {{> head}}
        {{> partial1}}
        {{> partial2}}
        {{> footer}}
    </body>
</html>

Implementation

Install dependencies

npm i -D handlebars xml2js

Add a hook to your config.xml, let's name it convertHbsToHtml.js. In your config.xml add this line

<hook src="convertHbsToHtml.js" type="after_prepare"/>

This works on every platform, thus do not include this line inside any specific platform. Insert it also before any hook you may have of html minification or html syntax verification, since hooks are run in order by which they appear in config.xml

Create a file convertHbsToHtml.js at the root directory (or whatever path you defined above in config.xml) with this content:

/* NodeJS script that uses handlebars to process the .hbs files */

// node/npm includes
const fs = require('fs')
const path = require('path')
const xml2js = require('xml2js')
const Handlebars = require('handlebars')

const mainIndexHbsFile = 'index.hbs' // with respect to www/ dir

const twoSpaces = '  '

module.exports = function (context) {
  console.log(`${context.hook} : ${path.relative(context.opts.projectRoot, context.scriptLocation)}`)

  var projectRoot = context.opts.projectRoot
  var platforms = context.opts.platforms
  return new Promise((resolve, reject) => {
    const configXmlFullPath = path.join(context.opts.projectRoot, 'config.xml')
    getIndexHtmlFileFromConfigXML(configXmlFullPath, (mainIndexHtmlFile) => {
      console.log(`${twoSpaces}Main index html source file got from config.xml is ${path.join('www', mainIndexHtmlFile)}`)

      for (var i = 0; i < platforms.length; i++) {
        const wwwDistDir = context.opts.paths[i]
        console.log(`${twoSpaces}Processing html files for ${platforms[i]} at ${path.relative(projectRoot, wwwDistDir)}`)
        convertHbsToHtmlSync(wwwDistDir, mainIndexHtmlFile)
      }
      resolve()
    })
  })
}

function convertHbsToHtmlSync (wwwDistDir, mainIndexHtmlFile) {
  var fullPathMainIndexHbsFile = path.join(wwwDistDir, mainIndexHbsFile)

  // Register Partials
  var partialsDir = path.join(wwwDistDir, 'html-partials')
  var filenames = fs.readdirSync(partialsDir)

  filenames.forEach(function (filename) {
    var matches = /^([^.]+).hbs$/.exec(filename)
    if (!matches) {
      return
    }
    var name = matches[1]
    var template = fs.readFileSync(path.join(partialsDir, filename), 'utf8')
    Handlebars.registerPartial(name, template)
    console.log(`${twoSpaces + twoSpaces}Registered partial ${name}.hbs`)
  })

  var source = fs.readFileSync(fullPathMainIndexHbsFile, 'utf8').toString()
  var template = Handlebars.compile(source)
  var output = template()
  fs.writeFileSync(path.join(wwwDistDir, mainIndexHtmlFile), output, 'utf8')
  console.log(`${twoSpaces + twoSpaces}html file ${mainIndexHtmlFile} created`)

  // source handlebars files should be deleted on dist dir
  fs.unlinkSync(fullPathMainIndexHbsFile)
  fs.rmdirSync(partialsDir, { recursive: true })
  console.log(`${twoSpaces + twoSpaces}handlebars files deleted from dist dir`)
}

// get main index file from config.xml: <content src="index.html"/>
function getIndexHtmlFileFromConfigXML (configXmlFullPath, callback) {
  var parser = new xml2js.Parser()
  fs.readFile(configXmlFullPath, function (err, data) {
    if (err) {
      console.error(Error(err))
      process.exit(1)
    }
    parser.parseString(data, function (err, result) {
      if (err) {
        console.error(Error(err))
        process.exit(1)
      }
      callback(result.widget.content[0].$.src)
    })
  })
}

Simply test it by doing cordova prepare and then check the content of dist directory (for example in Android platforms/android/app/src/main/assets/www) to be sure the html file is generated and the handlebars files are erased from the dist.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...