Различные типы данных в форме и базе данных и прямое и обратное преобразование

Я думал, что это будет легко, но, да... это было не так. Я уже разместил вопрос, который шел в том же направлении, но сформулировал другой вопрос.

Что я хочу сделать

У меня есть коллекция песен, которая имеет атрибут времени (время воспроизведения песни). Этот атрибут должен обрабатываться по-разному в проверке формы и проверке на сервере!

! Я хотел бы сделать это с тем, что мне предлагает автоформа (и simple-schema / collection2). Если это возможно...

  • в форме время должно быть введено и проверено как строка, которая соответствует регулярному выражению /^\d{1,2}:?[0-5][0-9]$/ (так что либо формат "мм: сс" или ммсс).
  • в базе данных он должен храниться как номер

Что я пытался сделать

1. "FormToDoc-путь"

Это мой javascript

// schema for collection
var schema = {
    time: {
        label: "Time (MM:SS)",
        type: Number // !!!
    },
    // ...
};
SongsSchema = new SimpleSchema(schema);
Songs.attachSchema(SongsSchema);

// schema for form validation
schema.time.type = String // changing from Number to String!
schema.time.regEx = /^\d{1,2}:?[0-5][0-9]$/;
SongsSchemaForm = new SimpleSchema(schema);

И это мой шаблон:

{{>quickForm
   id="..."
   type="insert"
   collection="Songs"
   schema="SongsSchemaForm"
}}

Мой желаемый рабочий процесс будет:

  1. время проверяется как строка с использованием схемы
  2. время конвертируется в секунды (число)
  3. время проверяется как число в бэкэнде
  4. песня хранится

И обратный путь.

Я сначала попытался использовать крюк formToDoc и преобразовал строку в секунды (число).

Эта проблема:

Я обнаружил, что проверка формы с помощью заданной схемы (для формы) происходит ПОСЛЕ преобразования в `formToDoc, так что это уже Number и проверка как String не удалась.
Вот почему я искал другой крюк, который срабатывает после проверки формы. Вот почему я пытался...

2. "Перед тем, как вставить"

Я использовал крючок before.insert и путь к базе данных сработал!

AutoForm.hooks({
    formCreateSong: {
        before: {
            insert: function (doc) {
                // converting the doc.time to Number (seconds)
                // ...
                return doc;
            }
        },

        docToForm: function (doc) {
            // convert the doc.time (Number) back to a string (MM:SS)
            // ...
            return doc;
        }
    }
});

Эта проблема:

Когда я реализовал update-форм, docToForm не был назван, поэтому в форме обновления было числовое значение (в секундах).

Вопросы:

  1. Как я могу сделать обратный путь из базы данных в форму, чтобы преобразование из секунд в строку MM:SS?
  2. Есть ли лучший способ, как справиться с этим вариантом использования (разные типы данных в форме проверки и бэкэнд-проверки)?

Я ищу "метеорный автоформ" способ решения этой проблемы.

Большое спасибо за чтение и, надеюсь, хороший ответ;-)

2 ответа

Простой ответ - не проверять строку, а проверять число, в которое преобразуется строка.

С Simpleschema все, что вы делаете, это создаете пользовательскую проверку. Эта пользовательская проверка будет захватывать строку, превращать ее в число, а затем проверять это число.

Затем, когда вы извлекаете его из базы данных, вам нужно взять это число и преобразовать его в строку. Симплесхема не делает этого изначально, но это достаточно просто сделать в вашей форме.

Теперь, если вы хотите стать модным, вот что я бы порекомендовал:

Добавить новые поля схемы:

SimpleSchema.extendOptions({
  userValue: Match.Optional(Function),
  dbValue: Match.Optional(Function),
});

Затем добавьте функцию к вашему time поле (хранится как поле даты):

    userValue: function () {
      return moment(this.value).format('mm:ss');
    },
    dbValue: function () {
      return timeToNumber(this.value);
    }

Затем создайте функцию, которая преобразует timeString в число (быстрый и грязный пример, вам придется добавить проверку ошибок):

function timeToNumber(str) {
  str.replace(':',''); //remove colon
  var mins = +str.substr(0,2);
  var secs = +str.substr(2,2);
  return mins * 60 + secs; 
}

Затем для проверки в реальном времени вы можете использовать schema.namedContext().validateOne, Чтобы обновить БД, просто отправьте timeToNumber(input.value),

Я чувствую себя как time действительно должен быть отформатирован внутри представления, а не внутри модели. Так вот схема для time Я бы использовал:

...
function convertTimeToSeconds (timeString) {
  var timeSplit = timeString.split(':')
  return (parseInt(timeSplit[0]) * 60 + parseInt(timeSplit[1]))
}

time: {
  type: Number,
  autoValue: function () {
    if(!/^\d{1,2}:?[0-5][0-9]$/.test(this.value)) return false
    return convertTimeToSeconds(this.value)
  }
}
...

Это имеет небольшой недостаток, конечно. Вы не можете использовать quickForm-помогите больше, но придется использовать autoForm,

Чтобы затем отобразить значение, я бы просто find songs а затем напишите помощника:

Template.registerHelper('formateTime', function (seconds) {
  var secondsMod = seconds % 60
  return [(seconds - secondsMod) / 60, secondsMod].join(':')
})

В вашем шаблоне:

{{ formatTime time }}
Другие вопросы по тегам