У меня много проблем при попытке изменить объект "this" из псевдо-класса javascript, когда он уже инициализирован - PullRequest
1 голос
/ 16 марта 2012

Прокрутите вниз до нижней части этого поста, чтобы увидеть обходное / возможное решение.

Это, вероятно, проще просто объяснить в исходном коде с комментариями.Проблема в том, что я не могу понять, как псевдо-классы работают вместе для выполнения задачи, которую я пытаюсь выполнить (объяснение в коде ниже).

Код разбит на 3 файла: lead.js, router.js и db.js.

Приличное количество строк кода, но большая часть комментариев.

[lead.js]

var bcrypt = require('bcrypt'),
    validators = require('../lib/validators'),
    utility = require('../lib/utility'),
    document = {};

var Lead = module.exports = function (db) {
  // Save a reference to the database.
  this.db = db;

  // Reference initial document.
  // This is totally wrong, not sure how to 'send' a variable to the constructor of a class
  // when I cannot add another param. Due to how I'm importing the db model, I won't know what
  // the document is until I fill out the form. I've also tried 'document' instead of 'Lead.document'.
  this.document = Lead.document;

  // Setup the document if it exists.
  // This also doesn't work.
  // Basically I want to be able to set up a document variable outside of this module (line #100),
  // Then pass it to this module after filling it up with values from a form.
  // Then based on what's been filled in, it would fix up (trim, convert to lower case)
  // some of the values automatically and default a few values that I'm not always going to pass.
  if (!document) {
    var salt = bcrypt.genSaltSync(10),
        hash = bcrypt.hashSync(utility.generatePassword(), salt);

    // Default values.
    if (!document.meta.createdAt) { this.document.meta.createdAt = Date.now(); }
    if (!document.login.password) { this.document.login.password = hash; }
    if (!document.login.role) { this.document.login.role = 'User'; }

    // Normalize a few values.
    this.document.login.email = document.login.email.toLowerCase().trim();
    this.document.contact.name.first = document.contact.name.first.trim();
    this.document.contact.name.last = document.contact.name.last.trim();
    this.document.contact.address.street = document.contact.address.street.trim();
    this.document.contact.address.city = document.contact.address.city.trim();
    this.document.contact.address.state = document.contact.address.state.trim();
    this.document.contact.address.zip = document.contact.address.zip.trim();
    this.document.contact.phone.home = document.contact.phone.home.trim();
  }
  // So in regards to the above code, the end result I'm looking for is...
  // I want to append some properties to the this.document reference when the document is empty (when I'm updating it, I won't set the document), 
  // and on new documents it will append a few default values/normalize all the fields.
};

Lead.prototype.validate = function(fn) {
  var errors = [];

  // Some validation rules I cut out to make this shorter.

  if (errors.length) return fn(errors);
  fn();
};

Lead.prototype.save = function(fn) {
  this.db.collection('leads', function(err, collection) {
    if (err) { fn(new Error({message: err})); }

    collection.insert(this.document, function(err, result) {
      return fn(err, result);
    });
  });
};

---

[route.js file]

  var db = require('../models/db');

  app.post('/register', function(req, res) {
    var data = req.body.lead || {};

    // Fill the document.
    var document = {
      meta: {
        host: req.headers.host,
        referer: req.headers.referer,
        createdIPAddress: req.connection.remoteAddress
      },
      login: {
        email: data.email
      },
      contact: {
        name: {
          first: data.first,
          last: data.last
        },
        address: {
          street: data.street,
          city: data.city,
          state: data.state,
          zip: data.zip
        },
        phone: {
          home: data.phone
        }
      }
    };

    // Write the document.
    db.lead.document = document;

    db.lead.validate(function(err) {
      if (err) {
        req.session.error = err;
        return res.redirect('back');
      }

      db.lead.save(function(err) {
        res.redirect('/register/success');
      });
    });
  });

---
[db.js]

var mongodb = require('mongodb'),
    server = new mongodb.Server('localhost', 27017),
    connection = new mongodb.Db('test', server);

connection.open(function(err, db) {});

module.exports =  {
  lead: new (require('./lead'))(connection)
};

Когда я запускаю это, мой валидатор всегда сообщаетчто пароль пуст, что имеет смысл.Я отправляю документ изначально в класс с пустым паролем (пароль генерируется случайным образом, а не поле формы) - проблема в том, что я понятия не имею, что делать с блоком кода if (! Document) ...чтобы правильно установить this.document.

Я надеюсь, что между комментариями и кодом вы сможете понять, что я пытаюсь сделать.Я застрял на этом некоторое время.

РЕДАКТИРОВАТЬ

Я немного изменил поток, чтобы получить решение.

Вdb.js, я экспортировал соединение, а не создавал непосредственно экземпляр (и будущие модели).

В файле router.js мне требуются файл db и lead, затем передается как соединение db, так идокумент в конструкторе Лида.Пример.

var lead = new Lead(db, document);

В файле lead.js это становится так же просто, как сделать this.document = document (аналогично db).Когда я отправляю новый запрос, к документу добавляются значения, которые я не отправляю из router.js (дата создания, случайный пароль и т. Д.), И все хорошо.

Этодостойный способ справиться с этим, или есть лучший способ реорганизовать это?

Ответы [ 3 ]

1 голос
/ 16 марта 2012

Это совершенно неверный способ, даже если этот код работает так, как вы хотите. В этом примере у вас есть синглтон лидерство. Запрашивая / регистрируя URL, вы хотите установить в поле «документ» этот синглтон. (ВАЖНО) Но запросы работают асинхронно. Абсолютно нет гарантии, что вы сохраните документ, который только что подтвердили. Потому что новый запрос может перезаписать его в ведущем объекте. Вы должны сделать эту логику в области запроса. Одна область для одного запроса. Не один для всех.

0 голосов
/ 16 марта 2012

Вам нужно прочитать об объектно-ориентированном программировании в Javascript .

Анонимная функция, которую вы определяете в верхней части кода , является функцией конструктора, поэтому в отношении свойства document, которое вы хотите в данный момент не инициализировать, просто введите что-то вроде:

this.document = null;

Затем, через некоторое время, когда вы создаете новый объект с помощью этого конструктора, вот так:

var myLead = new Lead(dbConnection);

У вас будет свойство myLead.document.

Однако в вашем коде есть много других проблем. Почему вы предполагаете, что в вашей библиотеке есть глобальная переменная document с соответствующими данными, которая определена как {}? Код в этом операторе if в конце вашего конструктора должен выполняться, когда свойство document установлено в другом вашем файле ниже, и ожидать, что this.document будет существовать.

0 голосов
/ 16 марта 2012

Вы изначально установили var document = {}, а {} не ложно.Лучше было бы установить в качестве начального значения document = null, а затем после проверки на !document установить document = {} перед назначением любых необходимых вам свойств.

...