troubleshooting Question

How do I populate this popup with the corresponding data?

Avatar of Bruce Gust
Bruce GustFlag for United States of America asked on
JavaScriptNode.js
10 Comments1 Solution125 ViewsLast Modified:
What follows is my ongoing attempt to learn Nodejs while simultaneously learning Nodejs. I've got a fair amount of a tutorial done, but I'm still baffled more than I am productive.

So, here's my question: I'm trying to figure out where the data is coming from that's showing up on a popup window. Up to this point, I've been able to mirror some of the syntax, but now I'm at a point where I've got to do more than just "dig."

Here's what I've got:

When you click on the button you see below (Edit Activity)...

edit button
...you're triggering a bunch of code to display a popup that looks like this:

note
To get the form to display...

Here's the link:
<a href="#" style="float:right;"><i class="fas fa-fw fa-trash"></i></a><a href="#" class="note-link" data-prospect-id="{{ data._id }}" data-route="companies" data-company-name="{{ data.name|e('html_attr') }}" data-user-id="{{ (data.owner is empty?'undefined':data.owner._id) }}" data-toggle="tooltip" title="Edit Activity" data-activity-type="note" data-activityId={{ activity._id }}" style="float:right;"><i class="fas fa-fw fa-pencil fa-pencil-alt"></i></a>

The "note-link" class triggers the "note-link" method which looks like this:

      
$(document).on('click', '.note-link', function () {
		console.log("boom");
		var id = (typeof $(this).attr('data-company-id') != 'undefined') ? $(this).attr('data-company-id') : $(this).attr('data-prospect-id');
        //var cId = $(this).attr('data-company-id'),
		var cId = id, 
		pId = $(this).attr('data-proposal-id'),
		type = $(this).attr('data-activity-type');
		route = ((typeof $(this).attr('data-route') == 'undefined') ? route : $(this).attr('data-route'));


        if (typeof cId == 'undefined' || typeof type == 'undefined') {
            CAMS.alert('Invalid Link', 'The selected link is not properly formatted. Please refresh the page or contact support.', 'error');
        }

        CAMS.showActivityForm(cId, pId, type);
        // Engage / Add Activity for filtered or bulk dataset
        $companySelect = $(document).find('select[name="company"].select2');
        if (typeof $companySelect != 'undefined') {
            if ($companySelect.length > 0) {
                if (cId == 'bulk') {
                    // add the select2 elements
                    CAMS.createCompanySelector($companySelect);
                    // check for a company id in the opts
                    var _company = pickedList_s();
                    if (typeof _company != 'undefined') {
                        if (Array.isArray(_company)) {
                            _company.forEach((item) => {
                                $companySelect.append('<option value="' + item.id + '" selected="selected">' + item.name + '</option>').trigger('change');
                            });
                        }
                    }
                } else {
                    $companySelect.closest('.form-group').remove();
                }
            }
        }
    });

It's the CAMS.showActivityForm that takes it to the next step and that looks like this:

this.showActivityForm = function (companyId, proposalId, type) {
		console.log(type);
        [b]var model = objectModels[type],[/b]

            fc = model.singular.substring(0, 1),
            config = {
                title: 'Add a' + ((fc == 'a' || fc == 'e' || fc == 'i' || fc == 'o' || fc == 'u') ? 'n' : '') + ' ' + ucwords(model.singular),
                directionsText: 'Complete the form below to add a new ' + model.singular + '.',
                fields: model.fields,
                onShow: function (self) {
                    // add in the additional events and plugins
                    // add in datetime support
                    self.$el.find('input.field-datetime').datepicker({
                        autoClose: true,
                        language: 'en',
                        timepicker: true,
                        dateFormat: 'mm/dd/yyyy',
                        timeFormat: 'h:ii AA',
                        showEvent: 'focus',
                        minutesStep: 5
                    });

                },
                ajax: {
                    path: '/companies/add-activity',
                    params: {
                        company: companyId,
                        proposal: proposalId,
                        type: type,
                    },
                    onComplete: function (resp, self) {
                        if (resp.error) {
                            self.addMsg(resp.msg);
                            return false;
                        }
                        companyId = (companyId == 'bulk' ? resp.data.id : companyId);
                        recordId = companyId;
                        $(window).trigger('open-company-details', [companyId, ((typeof route != 'undefined') ? route : '/companies/'), 'history']);
                    }
                }
            };

        // show the form
       [b] formIt.show(config);[/b]
    };

var model = objectModels[type], is going to reach out to "object-models.js" and find the "type" that matches, what in this case is, "note." That looks like this:

 
note: {
    singular: 'note/reminder',
    plural: 'note/reminders',
      fields: {
      activityType: {
        type: 'string',
        required: true,
        [b]options: {'call': 'Call Note', 'visit': 'Visit Note', 'reminder': 'Reminder'}[/b]
      },
      date: {
        type: 'datetime',
        required: true
      },
      notes: {
        type: 'string',
        required: true,
        multiline: true
          },
          company: {
              type: 'id',
              required: true,
              lookup: 'companies',
              display: '{{ name }}',
              class: 'select2'
          }
    }
  },

options: {'call': 'Call Note', 'visit': 'Visit Note', 'reminder': 'Reminder'} will dictate the fields that are displayed in the form. They'll be merged into the "opts" variable which then passed to the "formit.js" file in the last line of the "showActivityForm" method referenced earlier.

The "formit.js" code looks like this (you probably don't need all this, but here you go)

'use strict';

function FormIt(options) {
  // Create the defaults we will extend
  this.defaults = {
    fields: {},
    title: 'New Form',
    directionsText: 'Complete the form below.',
    directionsClass: 'lead',
    containerClass: 'mfp-hide white-popup white-popup-md',
    submitButtonText: '<i class="fas fa-fw fa-check"></i> Save',
    submitButtonClass: 'btn btn-success',
    cancelButtonText: 'Cancel',
    cancelButtonClass: 'btn btn-link',
    loadDataFromUrl: '',
    onShow: function(self){},
    onDataReceived: function(data, self){ return data; },
    onDataLoaded: function(data, self){},
    onValid: function(data, self){},
    onSubmit: function(data, self){}
  };

  // create a new form uniq id
  this.formId = this._uniqid();

  // establish the opts that will be used
  this.opts = {};
  this.$el;
}

FormIt.prototype.init = function() {
  // create the base container to use
  var html = '<div id="formit-' + this.formId + '" class="' + this.opts.containerClass + '">' +
      '<h3>' + this.opts.title + '</h3>' +
      '<hr />' +
      '<p class="' + this.opts.directionsClass + '">' + this.opts.directionsText + '</p>' +
      '<div class="formit-errors"></div>' +
      '<div class="loading">' +
        '<h3 class="text-center"><i class="fas fa-fw fa-cog fa-spin"></i> Loading...</h3>' +
      '</div>' +
      '<form class="needs-validation d-none" novalidate>' +
        '<div class="field-list"></div>' +
        '<hr />' +
        '<div class="buttons-footer">' +
          '<button type="submit" class="btn-formit-submit ' + this.opts.submitButtonClass + '">' + this.opts.submitButtonText + '</button>' +
          '<button type="button" class="btn-formit-cancel ' + this.opts.cancelButtonClass + '">' + this.opts.cancelButtonText + '</button>' +
        '</div>' +
      '</form>' +
    '</div>';

  if ($('#formit-' + this.formId).length === 0) {
    $('body').append(html);
  } else {
    $('#formit-' + this.formId).replaceWith(html);
  }

  this.$el = $('#formit-' + this.formId);

  // add in the events
  this._addEvents();
};

FormIt.prototype.show = function(opts) {
  // create the instance specific options
  this.opts = $.extend({}, this.defaults, opts);

  // ensure there are fields for the form
  if ( this._getObjectKeys(this.opts.fields).length === 0 ) {
    throw 'Invalid fields option provided.';
  }

  // delete any existing form
  $('div[id^="formit-"]').remove();

  // create the base form
  this.init();

  // build the html form
  this._buildForm();

  // open the form
  $.magnificPopup.open({
    alignTop: true,
    items: {
      type: 'inline',
      src: this.$el
    }
  });

  // hide the loading screen if we do not need to load any external data
  if (this.opts.loadDataFromUrl === '') {
    this.hideLoading();

    this.opts.onDataLoaded({}, this);
  } else {
    this.loadData();
  }

  // call the callback
  if (typeof this.opts.onShow == 'function') {
    this.opts.onShow(this);
  }
};

FormIt.prototype.close = function() {
  $.magnificPopup.close();
};

FormIt.prototype.getData = function() {
  var data = {},
      fields = this.$el.find('form').serializeArray();

  for (var i in fields) {
    data[ fields[i].name ] = fields[i].value;
  }

  return data;
};

FormIt.prototype.loadData = function() {
  // set a local reference to the object for inside the ajax callback
  var self = this;

  // show the loading screen
  this.showLoading();

  // look up the data
  $.get(this.opts.loadDataFromUrl, {}, function(resp){
    if (resp.error) {
      self.close();
      return CAMS.alert('Oh no!', resp.msg, 'error');
    }

    // set a reference
    var data = (typeof resp.data.rows != 'undefined') ? resp.data.rows[0] : resp.data;

    // call a lifecycle method
    data = self.opts.onDataReceived(data, this);

    // stop on false
    if (!data) {
      return self.close();
    }

    // load the form
    self._loadDataIntoForm(data);

    // show the form
    self.hideLoading();
  }, 'json').fail(function(){
    self.close();
    CAMS.alert('Oh no!', 'An unexpected error was encountered while trying to load your data. Please try again.', 'error');
  });
};

FormIt.prototype.reset = function(options) {

};

FormIt.prototype.addMsg = function(msg, type) {
  type = type || 'danger';

  this.clearMsg();

  this.$el.find('.formit-errors').append('<p class="alert alert-'+type+'">'+msg+'</p>');
};

FormIt.prototype.clearMsg = function() {
  this.$el.find('.formit-errors').empty();
};

FormIt.prototype.hideLoading = function() {
  this.$el.find('.loading').addClass('d-none');
  this.$el.find('form').removeClass('d-none');
};

FormIt.prototype.showLoading = function() {
  this.$el.find('form').addClass('d-none');
  this.$el.find('.loading').removeClass('d-none');
};

FormIt.prototype._addEvents = function() {
  var self = this;

  // add in the submission form
  this.$el.on('submit', 'form', function(e){
    var $f = $(this);

    e.preventDefault();
    e.stopPropagation();

    $f.addClass('was-validated');

    if ($f[0].checkValidity() === false) {
      self.addMsg('Please check the highlighted fields.');
      return false;
    }

    self.clearMsg();

    var data = self.getData(),
        resp;

    if (typeof self.opts.onValid == 'function') {
      resp = self.opts.onValid(data, self);

      if (resp === false) {
        return false;
      }
    }

    if (typeof self.opts.onSubmit == 'function') {
      resp = self.opts.onSubmit(data, self);

      if (resp === false) {
        return false;
      }
    }

    if (typeof self.opts.ajax == 'undefined') {
      self.close();
    }

    self._ajax();
  });

  this.$el.on('click', 'button.btn-formit-cancel', function(){
    self.close();
  });

};

FormIt.prototype._ajax = function() {
  if (typeof this.opts.ajax.path == 'undefined' || this.opts.ajax.path === '') {
    throw 'Invalid or missing ajax.path value.';
  }

  var self = this,
      $btn = this.$el.find('button[type="submit"]'),
      btnText = $btn.html(),
      params = (typeof this.opts.ajax.params != 'undefined') ? '&' + this._serialize(this.opts.ajax.params) : '',
      data = this.$el.find('form').serialize() + params;

  $btn.html('<i class="fas fa-fw fa-spinner-third fa-spin"></i> Processing...');

  $.post(this.opts.ajax.path, data, function(resp){
    if (typeof self.opts.ajax.onComplete == 'function') {
      var check = self.opts.ajax.onComplete(resp, self);

      if (check === false) {
        return false;
      }
    }

    self.close();
  }, 'json').fail(function(){
    if (typeof self.opts.ajax.onError == 'function') {
      self.opts.ajax.onError(self);
    }
  }).always(function(){
    $btn.html(btnText)
  });
};

FormIt.prototype._buildForm = function() {
  var fields = [];

  for (var fid in this.opts.fields) {
    fields.push( this._createField(fid, this.opts.fields[fid]) );
  }

  this.$el.find('form .field-list').empty().append( fields.join('') );
};

FormIt.prototype._getObjectKeys = function(obj) {
  var keys = [];

  for (var k in obj) {
    keys.push(k);
  }

  return keys;
}

FormIt.prototype._loadDataIntoForm = function(data) {
  var $field, model;

  for (var fid in data) {
    // load the field
    $field = $('#formit-' + fid);

    if ($field.length === 0) {
      continue;
    }

    // look for the field model
    model = this.opts.fields[fid];

    // load the value based on the type of field
    switch (true) {
      // look for a select2
      case (typeof model.lookup != 'undefined'):
        if (typeof data[fid] == 'object' && typeof data[fid]._id != 'undefined') {
          $field.empty().append('<option value="'+data[fid]._id+'">'+data[fid].text+'</option>');
        }
        break;

      default:
        $field.val( data[fid] );
        break;
    }
  }

  // call the loaded lifecycle method
  this.opts.onDataLoaded(data, this);
};

FormIt.prototype._createField = function(fid, field) {
  var name = field.name || fid,
      label = field.label || this._fieldLabelFromId(fid),
      placeholder = field.placeholder || label,
      classes = field.class || '',
      help = field.help || '',
      input = '';

  switch (true) {
    // static text
    case (field.type == 'static'):
      var htmlType = field.htmlType || 'p';

      return '<' + htmlType + ' class="' + classes + '">' + label + '</' + htmlType + '>';
      break;

    // check for a select2
    case (typeof field.lookup != 'undefined'):
      input = '<select id="formit-' + fid + '" name="' + name + '" class="form-control ' + classes + '"' + ((field.required) ? ' required' : '') + '></select>';
      break;

    // multiline string = textarea
    case (field.type == 'string' && typeof field.multiline != 'undefined' && field.multiline):
      input = '<textarea id="formit-' + fid + '" name="' + name + '" placeholder="' + placeholder + '" class="form-control ' + classes + '"' + ((field.required) ? ' required' : '') + '></textarea>';
      break;

    // string with contacts is a select2 calling the contacts of the associated company
    case (field.type == 'string' && typeof field.contacts != 'undefined' && field.contacts):
      var opts = ''; // this is a placeholder for when I implement loading data into the form

      input = '<select id="formit-' + fid + '" name="' + name + '" class="form-control contacts select2 ' + classes + '"' + ((field.required) ? ' required' : '') + ' data-route="/companies/contacts">' + opts + '</select>';
      break;

    // string with options = select
    case (field.type == 'string' && typeof field.options != 'undefined'):
      var opts = '',
          isArr = Array.isArray(field.options),
          val = '';

      for (var k in field.options) {
        val = (isArr) ? field.options[k] : k;

        opts += '<option value="' + val + '">' + field.options[k] + '</option>';
      }

      input = '<select id="formit-' + fid + '" name="' + name + '" class="form-control ' + classes + '"' + ((field.required) ? ' required' : '') + '>' + opts + '</select>';
      break;

    // string, double, integer, email, datetime
    default:
      let inputType = 'text';
      let attr = ' ';

      if (field.type == 'double' || field.type == 'integer') {
        inputType = 'number';
        attr = (field.type == 'double') ? ' step="0.01"' : ' ';
      } else if (field.type == 'email') {
        inputType = 'email';
      }

      if (field.type == 'datetime') {
        classes += ' field-datetime';
      }

      input = '<input type="' + inputType + '" id="formit-' + fid + '" name="' + name + '" placeholder="' + placeholder + '" class="form-control ' + classes + '"' + ((field.required) ? ' required' : '') + attr + '>';
      break;
  }

  return '<div class="form-group">' +
    '<label for="formit-' + fid + '" class="control-label">' + label + ((field.required) ? '<strong>*</strong>' : '') + '</label>' +
    input +
    ((help !== '') ? '<small class="text-muted"><i class="fas fa-fw fa-question-circle"></i> ' + help + '</small>' : '') +
  '</div>';
};

FormIt.prototype._fieldLabelFromId = function(fid) {
  return (fid + '')
    .replace(/^(.)|\s+(.)/g, function ($1) {
      return $1.toUpperCase()
    });
};

FormIt.prototype._serialize = function(obj, prefix) {
  var str = [],
    p;
  for (p in obj) {
    if (obj.hasOwnProperty(p)) {
      var k = prefix ? prefix + "[" + p + "]" : p,
        v = obj[p];
      str.push((v !== null && typeof v === "object") ?
        serialize(v, k) :
        encodeURIComponent(k) + "=" + encodeURIComponent(v));
    }
  }
  return str.join("&");
};

FormIt.prototype._uniqid = function(prefix, moreEntropy) {
  //  discuss at: http://locutus.io/php/uniqid/
  // original by: Kevin van Zonneveld (http://kvz.io)
  //  revised by: Kankrelune (http://www.webfaktory.info/)
  //      note 1: Uses an internal counter (in locutus global) to avoid collision
  //   example 1: var $id = uniqid()
  //   example 1: var $result = $id.length === 13
  //   returns 1: true
  //   example 2: var $id = uniqid('foo')
  //   example 2: var $result = $id.length === (13 + 'foo'.length)
  //   returns 2: true
  //   example 3: var $id = uniqid('bar', true)
  //   example 3: var $result = $id.length === (23 + 'bar'.length)
  //   returns 3: true

  if (typeof prefix === 'undefined') {
    prefix = ''
  }

  var retId
  var _formatSeed = function (seed, reqWidth) {
    seed = parseInt(seed, 10).toString(16) // to hex str
    if (reqWidth < seed.length) {
      // so long we split
      return seed.slice(seed.length - reqWidth)
    }
    if (reqWidth > seed.length) {
      // so short we pad
      return Array(1 + (reqWidth - seed.length)).join('0') + seed
    }
    return seed
  }

  var $global = (typeof window !== 'undefined' ? window : global)
  $global.$locutus = $global.$locutus || {}
  var $locutus = $global.$locutus
  $locutus.php = $locutus.php || {}

  if (!$locutus.php.uniqidSeed) {
    // init seed with big random int
    $locutus.php.uniqidSeed = Math.floor(Math.random() * 0x75bcd15)
  }
  $locutus.php.uniqidSeed++

  // start with prefix, add current milliseconds hex string
  retId = prefix
  retId += _formatSeed(parseInt(new Date().getTime() / 1000, 10), 8)
  // add seed hex string
  retId += _formatSeed($locutus.php.uniqidSeed, 5)
  if (moreEntropy) {
    // for more entropy we add a float lower to 10
    retId += (Math.random() * 10).toFixed(8).toString()
  }

  return retId
};

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
:

So I've got the form that's used to add a note. But what I need to do is populate the fields of the form with the info that corresponds to the already existing note.

How?

I've been able to find the "service" that's populating the list of companies, but I don't where to be looking for list of "activities," and then, once I find that list, I don't know how to write a query that's going to populate the form fields as they're being constructed in "formit.js"

What am I looking for and how do I get this done?
arrow.png
assign.png
note.png
Join the community to see this answer!
Join our exclusive community to see this answer & millions of others.
Unlock 1 Answer and 10 Comments.
Join the Community
Learn from the best

Network and collaborate with thousands of CTOs, CISOs, and IT Pros rooting for you and your success.

Andrew Hancock - VMware vExpert
See if this solution works for you by signing up for a 7 day free trial.
Unlock 1 Answer and 10 Comments.
Try for 7 days

”The time we save is the biggest benefit of E-E to our team. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange.

-Mike Kapnisakis, Warner Bros