如何使用final-form-calculate用带有全选复选框的复选框来装饰最终表单?

时间:2018-07-24 23:14:27

标签: javascript reactjs react-final-form

当需要选中“全选”复选框时,我需要选择/取消选择所有复选框的功能,而当取消选中任何一个或多个复选框时,则需要取消选择“全选”复选框的功能。

我需要使用final-form来执行此操作(因为如果使用状态,它会重新加载视图并重置表单中的其他字段)。到目前为止,我发现最好的解决方案是使用final-form-calculate装饰表单。

到目前为止,全选功能将选择并取消选择所有框,当所有框均被选中时,全选框将自动选择。问题在于,当全部选中后,您取消选择了一天中的一个复选框,然后取消选择了全选(这是所希望的),但因为它会触发装饰器中的全部字段,并取消选择所有复选框。

这是装饰器所在的我的容器代码:

import React from 'react'
import { connect } from 'react-redux'
import { Link } from '../../routes'
import { selectAvailability, selectUser, selectWorkExperience, updateUser } from '../../modules/users'
import WorkerUpdateForm from '../../components/WorkerUpdateForm'
import { mapFinalFormErrors } from '../../lib/utils'
import { Form } from 'react-final-form'
import { map, mapValues, merge, size } from 'lodash'
import { retrieveShiftTypes, selectShiftTypes } from '../../modules/shifts'
import { getStripeConnectUrl } from '../../modules/workers'
import createDecorator from 'final-form-calculate'

const defaultAvailability = {
  all: false,
  monday: false,
  tuesday: false,
  wednesday: false,
  thursday: false,
  friday: false,
  saturday: false,
  sunday: false,
}

const mapErrors = mapFinalFormErrors('Failed to update account')

const mapDispatchToProps = { updateUser, retrieveShiftTypes, getStripeConnectUrl }

const mapStateToProps = (state) => ({
  user: selectUser(state),
  workExperience: selectWorkExperience(state),
  shiftTypes: map(selectShiftTypes(state)),
  availability: selectAvailability(state),
})

const decorator = createDecorator(
  {
    field: 'all',
    isEqual: (lastValue, nextValue) => lastValue === nextValue,
    updates: {
      'day[0]': (value) => value,
      'day[1]': (value) => value,
      'day[2]': (value) => value,
      'day[3]': (value) => value,
      'day[4]': (value) => value,
      'day[5]': (value) => value,
      'day[6]': (value) => value,
    }
  },
  {
    field: /day\[\d\]/,
    updates: {
      all: (firstValue, allValues) => (allValues.day || true)
        .reduce((lastValue, nextValue) => lastValue && nextValue)
    }
  }
)

class WorkerDashboardContainer extends React.Component {
  state = {
    availability: {
      monday: false,
      tuesday: false,
      wednesday: false,
      thursday: false,
      friday: false,
      saturday: false,
      sunday: false,
    },
    pending: false,
  }

  componentDidMount() {
    const { retrieveShiftTypes, availability } = this.props
    if (availability !== null) {
      this.setState({ availability: availability })
    }
    retrieveShiftTypes()
  }

  openStripeConnect = (event) => {
    event.preventDefault()

    const { getStripeConnectUrl, user } = this.props

    this.setState({ pending: true })

    getStripeConnectUrl(user.profileable.id)
      .then(response => {
          window.location.href = response.url
      })
      .catch(() => (
        this.setState({ pending: false })
      ))
  }

  onSubmit = (values) => {
    const { updateUser, workExperience, user } = this.props

    const workerExperiencesAttributes = map(merge(
      {},
      workExperience,
      mapValues(values.workerExperiencesAttributes, (value, key) => ({
        numberOfYears: value,
        shiftTypeId: key.match(/^\d+/)[0],
      }))
    ))

    const updatedUser = {
      ...values,
      profileable_attributes: {
        id: user.profileable.id,
        zipCode: values.zipCode,
        availability: this.state.availability,
        workerExperiencesAttributes,
      },
    }

    return updateUser(user.id, updatedUser)
      .catch(mapErrors)
  }

  render() {

    const { user, workExperience, shiftTypes, availability } = this.props

    let initialValues

    if (size(workExperience) > 0) {
      initialValues = {
        workerExperiencesAttributes: mapValues(workExperience, 'numberOfYears')
      }
    } else {
      initialValues = {
        workerExperiencesAttributes: shiftTypes.reduce((accumulator, type) => ({
          ...accumulator,
          [`${type.id}_${type.name}`]: 0,
        }), {})
      }
    }

    let initialAvailability = availability ? availability : defaultAvailability

    initialValues = {
      ...initialValues,
      firstName: user.firstName,
      lastName: user.lastName,
      zipCode: user.profileable.zipCode,
      phoneNumber: user.phoneNumber,
      email: user.email,
      availability: initialAvailability,
    }

    return (
      <Form
        component={WorkerUpdateForm}
        onSubmit={this.onSubmit}
        handleLogout={this.props.handleLogout}
        openStripeConnect={this.openStripeConnect}
        initialValues={initialValues}
        shiftTypes={shiftTypes}
        stripeIsSetup={user.profileable.stripeIsSetup}
        decorators={[decorator]}
  />
    )
  }
}

WorkerDashboardContainer = connect(mapStateToProps, mapDispatchToProps)(WorkerDashboardContainer)

export default WorkerDashboardContainer

这是表格:

import React from 'react'
import { Button, FormGroup, HelpBlock, Image, Panel } from 'react-bootstrap'
import { Field } from 'react-final-form'
import styles from '../styles/rc-slider.css.js'
import FieldGroup from './FieldGroup'
import { email, required } from '../lib/validators'
import CheckboxAdapter from './CheckboxAdapter'
import { SliderAdapter } from './worker-registration/WorkExperienceForm'


const WorkerUpdateForm = ({handleSubmit, openStripeConnect, handleLogout, submitFailed, submitError, submitting, availability, shiftTypes, stripeIsSetup}) => (
  <Panel>
    <form onSubmit={handleSubmit}>
      <h4>Payment Information</h4>
      {stripeIsSetup ? (
        <div>
          <p>View your shift payouts and update your payment information on your Stripe Dashboard.</p>
          <Button
            block
            bsSize="lg"
            bsStyle="info"
            onClick={openStripeConnect}
          >View Stripe Dashboard</Button>
        </div>
        ) : (
        <div>
          <p>Qwick uses Stripe to get you paid quickly and keep your personal and payment information secure. Thousands of companies around the world trust Stripe to process payments for their users.</p>
          <Button
            block
            bsSize="lg"
            bsStyle="info"
            onClick={openStripeConnect}
          >Setup Stripe Now</Button>
        </div>
      )}
      <Image
        alt="Powered by Stripe"
        className="text-center"
        src="/static/images/powered_by_stripe.svg"
        responsive
      />
      <br />

      <h4>Your Information</h4>
      <Field
        id="firstName"
        name="firstName"
        label="First Name*"
        type="text"
        component={FieldGroup}
        validate={required}
      />
      <Field
        id="lastName"
        name="lastName"
        label="Last Name*"
        type="text"
        component={FieldGroup}
        validate={required}
      />
      <Field
        id="zipCode"
        name="zipCode"
        label="Zip Code*"
        type="number"
        component={FieldGroup}
        validate={required}
      />
      <Field
        id="mobileNumber"
        name="phoneNumber"
        label="Mobile Number*"
        type="tel"
        component={FieldGroup}
        validate={required}
      />
      <Field
        id="email"
        name="email"
        label="Email*"
        type="email"
        component={FieldGroup}
        validate={value => required(value) || email(value)}
      />
      <br />

      <h4>Work Experience</h4>
      {shiftTypes.map((type) => (
        <Field
          key={type.id}
          name={`workerExperiencesAttributes.${type.id}_${type.name}`}
          label={type.name}
          component={SliderAdapter}
        />
      ))}
      <br />

      <h4>Availability</h4>
      <Field
        id="all"
        name="all"
        label="Select All"
        component={CheckboxAdapter}
      />
      <Field
        id="monday"
        name="day[0]"
        label="Monday"
        component={CheckboxAdapter}
      />
      <Field
        id="tuesday"
        name="day[1]"
        label="Tuesday"
        component={CheckboxAdapter}
      />
      <Field
        id="wednesday"
        name="day[2]"
        label="Wednesday"
        component={CheckboxAdapter}
      />
      <Field
        id="thursday"
        name="day[3]"
        label="Thursday"
        component={CheckboxAdapter}
      />
      <Field
        id="friday"
        name="day[4]"
        label="Friday"
        component={CheckboxAdapter}
      />
      <Field
        id="saturday"
        name="day[5]"
        label="Saturday"
        component={CheckboxAdapter}
      />
      <Field
        id="sunday"
        name="day[6]"
        label="Sunday"
        component={CheckboxAdapter}
      />

      {submitFailed &&
      <FormGroup validationState="error">
        <HelpBlock>{submitError}</HelpBlock>
      </FormGroup>
      }
      <Button
        block
        bsSize="lg"
        bsStyle="link"
        className="logout"
        onClick={handleLogout}
      >Log Out</Button>
      <Button
        block
        bsSize="lg"
        bsStyle="primary"
        disabled={submitting}
        type="submit"
      >{submitting ? 'Saving...' : 'Save'}</Button>

      <style jsx global>{styles}</style>
      <style jsx>{`
      form :global(.checkbox){
        border-bottom: 1px solid #eee;
        padding: 0 0 20px 0;
        margin: 0 0 20px 0;
        display:block;
      }
      h4{
        margin: 20px 0 20px 0;
        width: 100%;
      }
      :global(main.main-wrap){
        padding-bottom: 120px;
      }
      :global(main.main-wrap .component-centered .btn-primary){
        bottom: 60px;
      }
      :global(.btn.logout){
        position: absolute;
        left: 0;
        bottom: 5px;
        margin: 0;
        letter-spacing: 0;
      }
      :global(.btn.logout:hover){
        text-decoration: none;
      }
      :global(img.text-center){
        margin-top: 15px;
      }
    `}</style>
    </form>
  </Panel>
)

export default WorkerUpdateForm

0 个答案:

没有答案