zspecza / common-tags

🔖 Useful template literal tags for dealing with strings in ES2015+
Other
1.99k stars 60 forks source link

Remove `null` values from `html` output #29

Closed zspecza closed 8 years ago

zspecza commented 8 years ago

Currently, the html tag produces invalid markup when a null value is encountered:

import {html} from 'common-tags'

const todos = [{
  task: 'some task',
  done: false
}, {
  task: 'other task',
  done: true
}]

html`
  <h1>Incomplete tasks:</h1>
  <ul>
    ${todos.map((t) => t.done ? null : `<li>${t.task}</li>`)}
  </ul>
`

Produces:

<h1>Incomplete tasks</h1>
<ul>
  <li>some task</li>
  null
</ul>
fromthemills commented 8 years ago

Was having a similar problem with undefined. Maybe this functionality should be implemented as a separate tag. A tag witch removes undefined, NaN and null and replaces them with empty string values. Atm I am using a lot of this syntax ${ className ? className : '' } or ${ className || '' } witch is not ideal.

zspecza commented 8 years ago

@DriesVandermeulen when I get some time, I'll try push a fix for this over the weekend :shipit: rather swamped at the moment.

In the meantime, you can do something like this (untested):

html.js

'use strict'

import {
  TemplateTag,
  stripIndentTransformer,
  inlineArrayTransformer,
  trimResultTransformer,
  splitStringTransformer
} from 'common-tags'

const isValidValue = (x) => x != null && x !== NaN

const removeNullSubstitutionTransformer = () => ({
  onSubstitution (substitution) {
    if (Array.isArray(substitution)) {
      return substitution.filter(isValidValue)
    }
    if (isValidValue(substitution)) {
      return substitution
    }
    return ''
  }
})

const html = new TemplateTag(
  splitStringTransformer('\n'),
  removeNullSubstitutionTransformer,
  inlineArrayTransformer,
  stripIndentTransformer,
  trimResultTransformer
)

export default html
shannonmoeller commented 8 years ago

I'd recommend ignoring boolean values as well. I often use the following pattern:

import { html } from 'common-tags';

export default ({ hasNext, hasPrevious }) => html`
    <fieldset>
        ${hasNext && html`
            <button step-next>Next</button>
        `}

        ${hasPrevious && html`
            <button step-prev>Previous</button>
        `}
    </fieldset>
`;

Where hasNext and hasPrevious are booleans, this pattern prints false when rendering the first and last steps because there is no next or previous. I may be the exception, but I almost never need to print a literal true or false value.

This would do it:

function isValidValue (x) {
    return x != null
        && !Number.isNaN(x)
        && typeof x !== 'boolean';
}