Что формирует заголовок в my = f.input: title? - PullRequest
0 голосов
/ 04 февраля 2019

У меня есть представление:

- entry = f.object
- kind = entry.new_record? ? '' : entry.kind

tr[class="entry_row f-order-entry f-order-nested-entry" data-kind=(kind.to_s)
   class=(entry.created_without_order? ? 'entry_no_order' : '')
   class=(entry.kind == Entry::COMPLEX ? 'f-ignore-price f-complex-order-entry' : '' )]

  td.new-entry
    - if entry.created_without_order?
      = image_tag('new-box.png', size: '30x25', alt: 'new')
  td.hidden

    = f.input :id
    = f.input :entry_type_id
    = f.input :clinic_id
    = f.input :client_id
    = f.input :order_id
    = f.input :kind
    = f.input :number
    = f.input :state
    = f.input :discount_disabled, as: :string
    = f.input :final_price, input_html: { class: 'f-order-entry-final-price' }
    = f.input :sum, input_html: { class: 'f-order-entry-sum' }
    = f.input :discount_sum, input_html: { class: 'f-order-entry-discount-sum' }
    = f.input :date, as: :string, input_html: { class: 'f-order-entry-date' }

    = new_fields_template(f, :members, entry.members.length)
    = new_fields_template(f,
                          :consumables,
                          entry.consumables.length,
                          nil,
                          variables: { parent: :entry })

  td.entry-title.tooltip-bottom

    = f.input :title, input_html: { readonly: true, title:  }
    = entry.title   
    .toggle-content-button
      span.toggle-content-button-number
      span.toggle-content-button-icon

  = edit_order_entry_performer f, @performers
  = edit_order_entry_assistant f, @assistants
  = edit_order_entry_referral f, @referrals
  = edit_order_entry_store f, @stores

эта строка

= f.input: title, input_html: {readonly: true, title:}

формирует следующую разметку

<input class="form-control string required" autocomplete="off" readonly="readonly" title="" type="text" name="order[entries_attributes][0][title]" id="order_entries_attributes_0_title">

мы получаем динамический список сервисов (элементы добавляются щелчком по каталогу), мне нужно получить атрибут title во входных данных, так же каксамо имя (имена длинные, не всегда умещаются на экране, всплывающая подсказка отображается позже из заголовка (это настроено, мне нужно подставить только значение, равное названию самого сервиса)

вот js, который рисует линии после добавления из каталога (каталога)

FormFactory.nestedFields = function (params) {
  //var form = params.form
  var msgPrefix = 'page.form.' + params.model + '.nestedFields.'

  var settings = params.nestedFields
  var template = params.form.find('._template_' + settings.model)

  var model = settings.model

  PubSub.subscribe(msgPrefix + 'add', function (msg, data) {
    var item = data.item
    var generated = template.railsTemplate(model)
    var fields = generated.fields
    var html = generated.html

    var response = $.extend(data, {
      html: html,
      fields: fields,
      item: item,
      msgFrom: msg
    })

    PubSub.publish(msgPrefix + 'render', response)
  })

  return {

  }
}

js, которые образуют всю таблицу (здесь заинтересован только исполнитель записей - название сервиса, которое мне нужно добавитьдо

FormFactory.orderEntryList = function (params) {
  var ENTRY_NOT_READY = 1
  const SELECT_UPDATE_DELAY = 300

  var msgPrefix = 'page.form.' + params.model + '.orderEntryList.'

  const form = params.form
  var container = params.orderEntryList.container
  var attributes = {}
  var entriesTemplate = $('._template_entries')
  var round = Utils.moneyRound

  //
  // core funcs
  //

  var init = function () {
    container.find('.entry_row').each(function (i, e) {
      var row = $(e).nestedFieldSet()
      if (gon.specific.order_locked) row.lockFromBillingChanges()
      row.init()
      row.hideContent()
      if (row.isComplex()) row.showContent()
    })

    triggerListChanged()
    updateSelects()
  }

  var updateIndexesTimeout
  var updateIndexes = function () {
    if (updateIndexesTimeout) clearTimeout(updateIndexesTimeout)

    // massive complex addition calls updateIndex too many times and freezes ui
    updateIndexesTimeout = setTimeout(function () {
      container[0].querySelectorAll('.entry_row').forEach(function (e, i) {
        var row = $(e).nestedFieldSet()
        row.setIndex(i)
        row.init()
      })
    }, 10)
  }

  var buildItem = function (proto) {
    let rez = {
      amount: proto.amount,
      assistant_id: attributes.assistant_id,
      client_id: attributes.client_id,
      clinic_id: attributes.clinic_id,
      date: attributes.date,
      discount_disabled: proto.discount_disabled,
      discount_percent: proto.discount_disabled
        ? 0
        : (attributes.discount_percent || 0),
      entry_type_id: proto.id,
      final_price: proto.price,
      kind: proto.kind,
      number: proto.number,
      price: proto.price,
      state: ENTRY_NOT_READY,
      store_id: attributes.store_id,
      title: proto.title,
      user_id: attributes.user_id,
      referral_id: attributes.referral_id,
      account: attributes.account
    }

    return rez
  }

  var triggerListChangedDelay = null
  var triggerListChanged = function () {
    if (triggerListChangedDelay) clearTimeout(triggerListChangedDelay)
    triggerListChangedDelay = setTimeout(function () {
      var result = {
        sum: 0,
        final_sum: 0,
        discountable_sum: 0
      }

      var calculateDiscountableSum = function (elem) {
        if (elem.isDeleted() || elem.get('discount_disabled', 'bool')) return
        result.discountable_sum += elem.get('sum', 'float')
      }

      container.find('.entry_row').each(function (i, e) {
        var row = $(e).nestedFieldSet()
        if (row.isDeleted()) return

        if (row.isComplex()) {
          row.getDataArray('members').forEach(calculateDiscountableSum)
        } else {
          calculateDiscountableSum(row)
        }

        result.sum += parseFloat(row.get('sum'))
        result.final_sum += parseFloat(row.get('final_sum'))
      })

      result.sum = round(result.sum)
      result.final_sum = round(result.final_sum)
      result.discount_sum = round(result.sum - result.final_sum)

      PubSub.emit('page.form.' + params.model + '.updateComponents')
      PubSub.emit(msgPrefix + 'listChanged', result)
    }, 10)
  }

  // remove passed attributes & undefineds
  var cleanAttributes = function (attrs) {
    var omitList = Array.prototype.slice.call(arguments, 1)
    attrs = _.omit.apply(_, [attrs].concat(omitList))
    return _.omit(attrs, _.isUndefined)
  }

  const postAdd = () => {
    updateIndexes()
    triggerListChanged()
    updateSelects()
  }

  var addEntry = function (entryType) {
    var entryProto = $.extend({}, entryType, {amount: 1})
    var entryAttributes = _.omit(buildItem(entryProto), _.isUndefined)
    var entryHtml = entriesTemplate.railsTemplate('entries').html
    var entryRow = entryHtml.first().nestedFieldSet()

    entryRow.set(entryAttributes)
    container.append(entryHtml)

    entryProto.entry_type_members.forEach(function (member) {
      addMember(member.member, {
        complexRow: entryRow,
        amount: member.amount,
        skipInit: true
      })
    })

    entryProto.entry_type_consumables.forEach(function (consumable) {
      addConsumable(consumable.consumable, {
        amount: consumable.amount,
        entryRow: entryRow
      })
    })

    entryRow.init()
    entryRow.hideContent()
    if (entryRow.isComplex()) entryRow.showContent()
    entryRow.recalculate()
    postAdd()
  }

  var addMember = function (entryType, params) {
    var complexRow = params.complexRow

    var memberHtml = complexRow
      .getMemberTemplate()
      .railsTemplate('members').html

    var memberRow = memberHtml.first().nestedFieldSet()
    var memberAmount = params.amount || 1
    var memberAttributes = buildItem($.extend({}, entryType, {
      amount: memberAmount
    }))

    memberAttributes = cleanAttributes(memberAttributes, 'order_id')

    if (complexRow.get('discount_disabled', 'bool')) {
      memberAttributes.discount_disabled = true
      memberAttributes.discount_percent = 0
    }

    memberRow.set(memberAttributes)
    complexRow.appendMemberHtml(memberHtml)

    entryType.entry_type_consumables.forEach(function (consumable) {
      addConsumable(consumable.consumable, {
        amount: consumable.amount * memberAmount,
        entryRow: memberRow
      })
    })

    if (!params.skipInit) {
      memberRow.hideContent()
      complexRow.init()
      postAdd()
    }
  }

  var addConsumable = function (entryType, params) {
    var entryRow = params.entryRow

    var consumableHtml = entryRow
      .getConsumableTemplate()
      .railsTemplate('consumables').html
    var consumableRow = consumableHtml.first().nestedFieldSet()

    var consumableAttributes = buildItem($.extend({}, entryType, {
      amount: params.amount || 1
    }))

    $.extend(consumableAttributes, {
      price: 0,
      final_price: 0,
      sum: 0,
      final_sum: 0,
      discount_percent: 0,
      discount_sum: 0,
      consumable: true
    })

    consumableAttributes = cleanAttributes(consumableAttributes,
      'order_id', 'user_id', 'assistant_id', 'referral_id'
    )

    consumableRow.set(consumableAttributes)
    entryRow.appendConsumableHtml(consumableHtml)

    entryRow.init()
    entryRow.redraw()
    consumableRow.redraw()
    postAdd()
  }

  var checkFieldsForNull = function (opts) {
    var attributes = {
      'performer': 'user_id',
      'assistant': 'assistant_id',
      'referral': 'referral_id'
    }
    return container.find('.f-order-entry').toArray().some(function (e, i) {
      var row = $(e).nestedFieldSet()
      var currentAttrs = row.get()
      return !currentAttrs[attributes[opts.change]]
    })
  }

  var setAttributes = function (opts) {

    container.find('.f-order-entry').each(function (i, e) {
      var row = $(e).nestedFieldSet()
      var newAttrs = _.clone(attributes)
      if (row.isConsumable()) {
        newAttrs = _.omit(newAttrs, 'user_id', 'assistant_id', 'referral_id')
      }
      var currentAttrs = row.get()

      if (row.get('discount_disabled', 'bool')) delete newAttrs.discount_percent
      if (row.get('price') == 0) delete newAttrs.discount_percent

      if (currentAttrs.user_id && !(opts.change === 'performer')) delete newAttrs.user_id
      if (currentAttrs.assistant_id && !(opts.change === 'assistant')) delete newAttrs.assistant_id
      if (currentAttrs.referral_id && !(opts.change === 'referral')) delete newAttrs.referral_id
      if (!currentAttrs.state && !(opts.change === 'state')) newAttrs.state = ENTRY_NOT_READY

      if (!opts.force) {
        if (currentAttrs.discount_percent && !(opts.change === 'discount')) delete newAttrs.discount_percent
      }

      row.set(newAttrs)
    })

    container.find('.entry_row').each(function (i, e) {
      $(e).nestedFieldSet().recalculate()
    })

    triggerListChanged()
  }

  var openHiddenErrors = function (e, data) {
    var entriesErrors = data.errors.entries_attributes
    if (!entriesErrors) return

    var entries = container.find('.entry_row').toArray().map(function (e) {
      return $(e).nestedFieldSet()
    })

    Object.keys(entriesErrors).forEach(function (entryIndex) {
      var entry = entries[parseInt(entryIndex)]
      if (!entry) return

      var entryErrors = entriesErrors[entryIndex]
      var membersErrors = entryErrors.members_attributes

      if (membersErrors) {
        Object.keys(membersErrors).forEach(function (memberIndex) {
          var member = entry.getDataArray('members')[parseInt(memberIndex)]
          let memberErrors = membersErrors[memberIndex]
          let consumableErrors = memberErrors.consumables_attributes
          if (member && consumableErrors) member.showContent()
        })
      } else {
        let consumableErrors = entryErrors.consumables_attributes
        if (consumableErrors) entry.showContent()
      }
    })
  }

  //
  // PubSub subscriptions
  //

  PubSub.on(msgPrefix + 'askAddEntry', function (msg, data) {
    if (!data.selector) return addEntry(data.item)

    var selectorRow = data.selector.closest('tr')
    var complex = selectorRow.data('complex')
    var entry = selectorRow.data('entry')
    var type = selectorRow.data('type')

    switch (type) {
      case 'members':
        addMember(data.item, {
          amount: parseInt(complex.get('amount')),
          complexRow: complex
        })
        break
      case 'consumables':
        addConsumable(data.item, {
          amount: parseInt(entry.get('amount')),
          entryRow: entry
        })
        break
      default:
        addEntry(data.item)
    }

    if (gon.application.use_tips_in_orders) { tipNotify(data.item) }

  })

  PubSub.on(msgPrefix + 'askSetAttributes', function (msg, data) {
    attributes = $.extend(
      attributes,
      _.omit($.extend({}, data.attributes), _.isUndefined)
    )

    if (checkFieldsForNull(data) || !data.change) {
      setAttributes({force: false})
    } else {
      bootbox.confirmYN(t('change_field') + ' ' + t(data.change) + ' ' + t('for_all_positions'), function (res) {
        if (res) {
          setAttributes({force: false, change: data.change})
        }
      })
    }
  })

  PubSub.on(msgPrefix + 'askForceAttributes', function (msg, data) {
    //debugger
    attributes = $.extend(
      attributes,
      _.omit($.extend({}, data.attributes), _.isUndefined)
    )
    setAttributes({force: true})
  })

  PubSub.on('page.form.' + params.model + '.setNew', init)
  PubSub.on('page.form.' + params.model + '.setEdit', init)
  PubSub.on('page.form.' + params.model + '.submitError', openHiddenErrors)

  //
  // events
  //

  var recalculateSelector =
    '.f-order-entry-price, .f-order-member-price, ' +
    '.f-order-entry-amount, .f-order-member-amount, ' +
    '.f-order-entry-discount-percent, .f-order-member-discount-percent, ' +
    '.f-entry-referral'
  container.on('keyup change mouseup', recalculateSelector, function () {
    var row = $(this).closest('tr').nestedFieldSet()
    if (row.getName() === 'members') {
      row.data('complex').recalculate()
    } else {
      row.recalculate()
    }

    triggerListChanged()
  })

  container.on('keyup change mouseup', '.f-order-entry-amount', function () {
    var row = $(this).closest('tr').nestedFieldSet()

    if (row.getName() === 'members') row.data('complex').updateSchema()
    if (row.getName() === 'consumables') row.data('entry').updateSchema()

    row.updateContentItems()
    triggerListChanged()
  })

  container.on('click', '.toggle-content-button', function () {
    var row = $(this).closest('tr').nestedFieldSet()
    if (row.enableConsumableSelector()) {
      PubSub.emit('page.form.' + params.model + '.updateComponents')
    }

    row.toggleContent()
  })

  container.on('click', '.f-nested-destroy', function (e) {
    e.preventDefault()
    var row = $(this).closest('tr').nestedFieldSet()
    var entry = row.data('entry')
    if (entry) {
      entry.getConsumableTemplate().railsTemplate('consumables', 'reduceIndex')
    }
    row.gracefulDestroy()
    updateIndexes()
    if (entry) entry.redraw()
    triggerListChanged()
  })

  const updateSelects = () => {
    const selector = '.f-entry-performer, .f-entry-assistant, .f-entry-referral'
    const selectorStore = '.f-order-entry-store'

    setTimeout(() => {
      $(selector).select2({
        dropdownAutoWidth: true,
        templateSelection: Utils.userDropdownTemplate,
        width: '100%'
      })
    }, SELECT_UPDATE_DELAY)

    setTimeout(() => {
      $(selectorStore).select2({
        dropdownAutoWidth: true,
        width: '100%'
      })
    }, SELECT_UPDATE_DELAY)
  }

  const buildReferralOption = (item) => {
    const option = document.createElement('option')
    option.text = item.short_name
    option.value = item.id

    return option
  }

  PubSub.on('page.form.order.referralField.newOption', (msg, item) => {
    // add to all nodes
    form.find('.f-entry-referral').each((i, select) => {
      select.add(buildReferralOption(item), 0)
    })

    attributes = $.extend(
      attributes,
      {referral_id: item.id}
    )
    setAttributes({force: false})

    // add to template
    entriesTemplate[0]
      .content.querySelector('.f-entry-referral')
      .add(buildReferralOption(item), 0)
  })

  return {}
}

вот моя

запись отладки

--- !ruby/object:Entry
concise_attributes:
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: entry_type_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: created_at
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: updated_at
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: title
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: order_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: client_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: state
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: price
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: amount
  value_before_type_cast: '1'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: stateful
  value_before_type_cast: 'false'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: stackable
  value_before_type_cast: 'true'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: clinic_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: pending_data
  value_before_type_cast: 'false'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: sum
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: final_sum
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: discount_sum
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: discount_percent
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: user_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: store_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: kind
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: data
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: machine_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: complex_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: analysis_laboratory_id
- !ruby/object:ActiveRecord::Attribute::FromUser
  name: deleted_at
  original_attribute: !ruby/object:ActiveRecord::Attribute::FromDatabase
    name: deleted_at
    type: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
      delegate_dc_obj: !ruby/object:ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime
        precision: 
        scale: 
        limit: 
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: created_by_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: updated_by_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: deleted_by_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: assistant_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: date
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: cost_price
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: be_result
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: comment
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: final_price
  value_before_type_cast: '0.0'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: number
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: entry_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: consumable
  value_before_type_cast: 'false'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: template_data
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: template_html
  value_before_type_cast: ''
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: discount_disabled
  value_before_type_cast: 'false'
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: is_protokol_save
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: referral_id
- !ruby/object:ActiveRecord::Attribute::FromDatabase
  name: print_entry_info
  value_before_type_cast: 'true'
new_record: true
active_record_yaml_version: 2

debug entry.title = nill, я не знаю почему

Можете ли вы объяснить, как формируется входное имя и как вставить в него атрибут title с его именем?

1 Ответ

0 голосов
/ 22 апреля 2019
{ readonly: true, title:  }

это недопустимый синтаксис ruby, у вас, вероятно, есть nil или "".

также в js, вы можете сделать что-то вроде document.querySelector('input').title, чтобы получить атрибут titleвход.

...