8-Bit Borges
8-Bit Borges

Reputation: 10033

How can I get a value from index.html and use it to render a second template after parsing JSON file?

I'm trying to wrap my head around gulp and modern JavaScript bundling.


The application is pretty simple:

  1. A html page is displayed to the user, where he must check a few radio buttons.
  2. User then submits his selections clicking on a button.
  3. Upon choice submit, a JSON file must be parsed, in order to display one amongst 20 possible html pages

I have the following (simplified) structure:

dist/
    index.html
src/
    scripts/
          main.js
          modules/
                 dispatchForm.js
    styles/  
          index.scss
    templates/
          index.pug
          partials/
                  default.pug
                  selectors.pug
gulpfile.js
data.json

In my gulpfile.js, I have:

const bundler = () => {
  return rollup({
    input: './src/scripts/main.js',
    plugins: [
      babel(pkg.babel),
      nodeResolve(),
      commonJS(),
    ],
  }).then((bundle) => bundle.write({
    file: '.tmp/bundle.js',
    format: 'umd',
    sourceMap: 'inline',
  }));
};

const uglify = () => {
  return src('.tmp/bundle.js', {sourcemaps: true})
      .pipe(plugins.uglify())
      .pipe(dest('.tmp'))
      .pipe(plugins.size({title: 'bundler minified'}));
};

const styles = () => {
  const AUTOPREFIXER_BROWSERS = [
    'ie >= 10',
    'ie_mob >= 10',
    'ff >= 30',
    'chrome >= 34',
    'safari >= 7',
    'opera >= 23',
    'ios >= 7',
    'android >= 4.4',
    'bb >= 10',
  ];

  return src([
    'src/styles/main.scss',
    'src/styles/**/*.css',
  ])
      .pipe(plugins.sassGlob())
      .pipe(plugins.sass({
        precision: 10,
      }).on('error', plugins.sass.logError))
      .pipe(plugins.autoprefixer(AUTOPREFIXER_BROWSERS))
      .pipe(dest('.tmp'))
      .pipe(plugins.if('*.css', plugins.cssnano()))
      .pipe(dest('.tmp'))
      .pipe(plugins.size({title: 'styles'}));
};

// Uses PUG as template
const templates = (env) => () => {
  return src('./src/templates/*.pug')
      .pipe(plugins.pug({locals: {
        title: pkg.title,
        description: pkg.description,
        env,
      }}))
      .pipe(dest('dist'))
      .pipe(plugins.size({title: 'templates'}));
};

const reload = (done) => {
  server.reload();
  return done();
};


const images = (env) => () => {
  const destination = env === 'deploy' ? 'dist' : '.tmp';

  return src('./src/images/**/*.{gif,jpg,png,svg}')
      .pipe(dest(destination))
      .pipe(plugins.size({title: 'size'}))
};


const serve = () => {
  server.init({
    notify: false,
    logPrefix: 'WSK',
    scrollElementMapping: ['main', '.mdl-layout'],
    server: ['.tmp', 'dist'],
    port: 3000,
  });

  watch(
    ['src/**/*.pug'],
    series(templates('development'), reload)
  );

  watch(
    ['src/styles/**/*.{scss,css}'],
    series(styles, templates('development'), reload)
  );

  watch(
    ['src/scripts/main.js', 'src/scripts/**/*.js'],
    series(bundler, templates('development'), reload)
  );

  watch(
    ['src/images/**/*.{gif,jpg,png,svg}'],
    series(images('development'), templates('development'), reload)
  );
};

const clean = () => del(['.tmp', 'dist/*', '!dist/.git'], {dot: true});

exports.default = series(
    clean,
    bundler,
    uglify,
    styles,
    templates('deploy'),
    images('deploy')
);

exports.serve = series(
    bundler,
    styles,
    templates('development'),
    images('development'),
    serve
);

The way I understand it, after cleaning the files, the bundler will:


Main.js

import dispatchForm from './modules/dispatchForm';

const domContentLoad = (fn) => {
  if (document.readyState !== 'loading') fn();
  else document.addEventListener('DOMContentLoaded', fn);
};

domContentLoad(() => {
  dispatchForm();
});

dispatchForm.js

const button = document.querySelector('[data-calculator-button]');

function updateValue() {
  const gain  = document.querySelector('[data-calculator-form][name="gain"]:checked');
  const cost  = document.querySelector('[data-calculator-form][name="cost"]:checked');

  if (gain && cost) {
    button.removeAttribute('disabled');
    button.classList.remove('selectors__button--inactive');
  } else {
    button.setAttribute('disabled', '');
    button.classList.add('selectors__button--inactive');
  }
}

function dispatchForm() {
  const radioInput = document.querySelectorAll('[data-calculator-form]');
  radioInput.forEach(element => element.addEventListener('input', updateValue));
}

export default dispatchForm;

selectors.pug

...

.selectors__form
    .selectors__radio
      input.selectors__input(type='radio' id='gain-points' name='gain' value='points' data-calculator-form)
      label.selectors__label(for='gain-points')

    .selectors__radio
      input.selectors__input(type='radio' id='gain-money' name='gain' value='money' data-calculator-form)
      label.selectors__label(for='gain-money')

.selectors__form
for value in [70, 80, 90, 100, 110, 120, 130]
  .selectors__radio
    input.selectors__input(type='radio' id=`have-${value}` name='cost' value=value data-calculator-form)
    label.selectors__label(for=`have-${value}`)
      | Até 
      b= ` C$${value}`

button.selectors__button.selectors__button--calculate.selectors__button--inactive(disabled=true data-calculator-button)
...

The above creates some selectors for 'cost' or 'gain' from selectors.pug, main.js and dispatchForm.js, via gulps 'bundler', and renders it as html.


But now I would like to:

  1. Use one of the two button submitted values (${value}) and pass it as an argument to a function that will parse a JSON file.

  2. Finally, the parsed JSON result will be passed to another pug file


Questions

  1. How do I get this JSON (from dispatchForm.js? from gulpfile.js? from pug natively?) and pass it to another pug template?

  2. Should JSON fetching be dealt with on separate JS module, since displayed JSON will be rendered on a separate html page, using another pug template partial? How so?

  3. Should all possible second page html files be generated at build time and JS be used to display only the one based on the submitted value? Or should this second html page be rendered dinamically?


gulp-data

I also learned that there are packages like gulp-data, used to handle json data, and I don't know if they are the way to go here.


Also, this SO question hints as how to pass pug JSON objects to client side JavaScript.

Upvotes: 1

Views: 516

Answers (1)

johncomposed
johncomposed

Reputation: 99

The way you've phrased this question makes me think your main issue is conflating the build step with the application "runtime" (as in, when users are using your application), like when dispatchForm.js would ever be run. Gulp is a tool to generate out your project, but this is meant to happen way before any users are interacting with your site.

The SO question you linked is having express render the pug pages at "runtime", which, architecturally, is pretty different from having it happen at the build step with gulp.

If I'm understanding what you want correctly, off the top of my head there's 3 main ways to do it. The first would be by having the client-side JS manipulate the dom and change the page appropriately. You could use pug for this, rendered into a JS library via something like rollup-plugin-pug (found via this SO answer).

The second would be having this be an api call to a server, which then renders a pug template (which is what the SO question you linked is doing).

Or third, you could do something like rendering out all possible pages you'd want your users to go to at build time, and just make the dispatchForm.js just be sending them to the appropriate pages. In this case I'd recommend defining out these options in one place (in say, a json file) and having that be the source of a gulp pipeline. It gets a little zany having gulp generate multiple files from a single file, but you can find a variety of ways people have done something similar like this github thread, this Stack Overflow answer, this gist, etc.

Edit

If you want stuff to happen "upon choice submit", that's way 1. So using rollup-plugin-pug, it would look something like (completely untested and off the top of my head):

//- template.pug
.content
  p= gain
  p= cost

// modules/calculateButton.js
import templateFn from '../../templates/partials/template.pug';
 
const button = document.querySelector('[data-calculator-button]');
button.addEventListener('click', (e) => {
   if (e.target.getAttribute('disabled')) return;
   const gain  = document.querySelector('[data-calculator-form][name="gain"]:checked').value;
  const cost  = document.querySelector('[data-calculator-form][name="cost"]:checked').value;
  
  document.getElementById("result").innerHTML = templateFn({
    gain, cost
  });
});

Otherwise nothing's parsed on submit. For examples of the 3rd way, I'd recommend checking out the links I sent above. A lot of build scripts is just finding your own way to do things that has the right amount of power/complexity balanced with ease of maintenance.

Upvotes: 0

Related Questions