troubleshooting Question

How can I update the parent of a parent?

Avatar of Bruce Gust
Bruce GustFlag for United States of America asked on
JavaScriptNode.js
5 Comments1 Solution38 ViewsLast Modified:
Here's my starting point:

click here
When you click on "Loose Cannon Fitness," you're clicking on a link that triggers the "/companies" route. This is part of a more verbose method that's loading up a modal with all of the info...

 
/* ################################################################### */
    /* Global method for opening up the company details view  */
    /* ################################################################### */
    this.showCompanyDetails = (companyId, opts) => {
      const route =
        typeof opts.tempRoute !== 'undefined'
          ? opts.tempRoute.replace(/\//g, '')
          : 'companies';
      const sWidth = window.innerWidth > 550 ? 550 : window.innerWidth - 30;
      const afterOpen = opts.afterOpen || function afterOpen() {};

      $.get(`/${route}/${companyId}`, { ajax: 1 }, resp => {
        // add the content to the modal
        $('#modal-company')
          .find('.slide-panel-content')
          .empty()
          .append(resp);

        // add the content scrollbar
        SimpleScrollbar.initEl(
          $('#modal-company').find('.slide-panel-content')[0]
        );

        // enable tooltips
        $('#modal-company [data-toggle="tooltip"]').tooltip({
          container: $('#modal-company')
        });

        // show the slide panel
        $slider = $('<a href="modal-company" />')
          .bigSlide({
            menu: '#modal-company',
            menuWidth: `${sWidth}px`,
            easyClose: false,
            side: 'right',
            beforeOpen() {
              $('#modal-company').draggable();
            },
            afterOpen
          })
          .trigger('click');

        $('#modal-company').css('display', 'block');
      }).fail(() => {
        self.alert(
          'Error',
          'An unexpected error was encountered while trying to load your company record. Please refresh the page and try again.',
          'error'
        );
      });
    };

And here's the twig where all that info is being funneled to...

<!-- ####################################################################### -->
<!-- This template is meant to be used via AJAX and injected into a modal -->
<!-- ####################################################################### -->

{% include 'partials/includes/flash.html.twig' with {
  flash: msg
} %}

{% if error == false %}
  <header>
    <div class="row top-buttons">
      <div class="col text-left">
        <button type="button" id="btn-close" class="btn btn-light btn-sm">
          <i class="fas fa-fw fa-times"></i>
          Close
        </button>
        {% if data.tags is not empty %}
          {% for t in data.tags %}
            <span class="badge badge-info">{{ t|e }}</span>
          {% endfor %}
        {% endif %}
      </div>
      <div class="col text-right">
        <button type="button"
          id="btn-add"
          class="btn btn-light btn-sm company-add-activity"
          data-prospect-id="{{ data._id|e('html_attr') }}"
          data-route="companies"
          data-company-name="{{ data.name|e('html_attr') }}"
          data-user-id="{{ (data.owner is empty
            ? 'undefined'
            : data.owner._id)|e('html_attr') }}"
          data-toggle="tooltip"
          title="Add Activity">
          <i class="fas fa-fw fa-plus fa-plus-h"></i>
        </button>
        <button type="button"
          id="btn-edit"
          class="btn btn-light btn-sm"
          data-prospect-id="{{ data._id|e('html_attr') }}"
          data-route="companies"
          data-toggle="tooltip"
          title="Edit Company">
          <i class="fas fa-fw fa-pencil fa-pencil-alt"></i>
        </button>
        <button type="button"
          id="btn-delete"
          class="btn btn-light btn-sm"
          data-prospect-id="{{ data._id|e('html_attr') }}"
          data-route="companies"
          data-toggle="tooltip"
          title="Delete Company">
          <i class="fas fa-fw fa-trash"></i>
        </button>
      </div>
    </div>

    <h2>
      {{ data.name|e }}
    </h2>
    <ul class="nav nav-pills nav-pills-slide-out nav-fill"
      id="company-tabs"
      role="tablist">
      <li class="nav-item">
        <a class="nav-link active"
          id="details-tab"
          data-toggle="tab"
          href="#details-panel"
          role="tab"
          aria-controls="details"
          aria-selected="true">
          Details
        </a>
      </li>
      <li class="nav-item">
        <a class="nav-link"
          id="history-tab"
          data-toggle="tab"
          href="#history-panel"
          role="tab"
          aria-controls="history"
          aria-selected="false">
          History
        </a>
      </li>
      <li class="nav-item">
        <a class="nav-link"
          id="contacts-tab"
          data-toggle="tab"
          href="#contacts-panel"
          role="tab"
          aria-controls="contacts"
          aria-selected="false">
          Contacts
        </a>
      </li>
      <li class="nav-item">
        <a class="nav-link"
          id="files-tab"
          data-toggle="tab"
          href="#files-panel"
          role="tab"
          aria-controls="files"
          aria-selected="false">
          Files
        </a>
      </li>
    </ul>
  </header>

  <div class="body">
    <hr />
    <div class="tab-content" id="myTabContent">
      {% include './partials/company-slide-out/history-tab.html.twig' %}

      {% include './partials/company-slide-out/details-tab.html.twig' %}

      {% include './partials/company-slide-out/contacts-tab.html.twig' %}

      {% include './partials/company-slide-out/files-tab.html.twig' %}
    </div>
  </div>
{% endif %}


The "history-tab.html.twig" piece looks like this:

history tab
...and the code looks like this:

<!-- ############################################################## -->
<!-- Start the history tab -->
<!-- ############################################################## -->
<div class="tab-pane fade"
  id="history-panel"
  role="tabpanel"
  aria-labelledby="history-tab">
  {% if data.activity is empty %}
    <p class="lead text-center">
      This company does not have any associated activity/history.
    </p>
  {% endif %}

  <div class="container">
    <div class="timeline timeline-global">
      {% for row in data.activity %}
        <div class="timeline-month" title="{{ row._id|e('html_attr') }}">
          {{ row.prettyDate|e }}
          <span>
            {{ row.activities|length }}
            Entries
          </span>
        </div>
        {% for activity in row.activities|sort|reverse %}
          <div class="timeline-section">
            <div class="row">
              <div class="col">
                <div class="timeline-box">
                  <div class="box-title">
                    <i class="fa {{ activity.icon|e('html_attr') }}"
                      aria-hidden="true">

                    </i>
                    <a href="javascript:void(0);"
                      class="user-info"
                      data-user-id="{{ activity.user._id|e('html_attr') }}">
                      <i>
                        {{ activity.user.firstName|e }}
                        {{ activity.user.lastName|e }}
                      </i>
                    </a>
                    <span id="type-{{ activity._id }}">{{ activity.type|e }}</span>
                    <span title="{{ activity.date|date('Y-m-d H:i A') }}"
                      data-toggle="tooltip"
                      data-time="{{ activity.date|e('html_attr') }}">
                      {{ activity.prettyDate|e }}
                    </span>
                  </div>
                  <div class="box-content">
                    <a class="btn btn-sm btn-default float-right"
                      data-toggle="collapse"
                      href="#act-{{ activity._id }}"
                      role="button"
                      aria-expanded="false"
                      aria-controls="act-{{ activity._id }}">
                      Details
                    </a>
                    <div class="collapse" id="act-{{ activity._id }}">
                      {% for key, val in activity.meta %}
                        <div class="box-item">
							{% if key=="date" %}
								<span id="date-{{activity._id}}">{{val|date("M jS, Y \\a\\t g:i a")}} </span>
							{% elseif key=="notes" %}
								<strong>{{key}}</strong>: <span id="notes-{{activity._id}}">{{val}}</span>
							{% else %}
								<strong>{{key}}</strong>: <span>{{val}}</span>
							{% endif %}	
							{% if key=="notes" %}
								<br>
								<a href="#" 
									style="float:right;" 
									class="note-delete" 
									data-activityId="{{activity._id}}" 
									data-toggle="tooltip" 
									title="Delete Activity">
									<i class="fas fa-fw fa-trash"></i>
								</a>
								<a href="#" 
									class="note-link" 
									data-prospect-id="{{ data._id }}"
									data-dashboard="no" 
									data-activity-type="note" 
									data-route="companies" 
									data-company-name="{{ data.name|e('html_attr') }}" 
									data-user-id="{{ (data.owner is empty?'undefined':data.owner._id) }}" 
									data-activityId="{{activity._id}}" 
									data-toggle="tooltip" 
									title="Edit Activity" 
									style="float:right;">
									<i class="fas fa-fw fa-pencil fa-pencil-alt"></i>
								</a>
							{% endif %}
                        </div>
                      {% endfor %}
                    </div>
                    {% if activity.proposal is not empty %}
                      <div class="box-item">
                        <strong>Related to</strong>:
                        <a href="javascript:void(0);"
                          class="open-proposal-details"
                          data-proposal-id="{{ activity.proposal|e(
                            'html_attr'
                          ) }}"
                          data-route="companies">
                          Proposal
                        </a>
                      </div>
                    {% endif %}
                    <div class="clearfix clear-fix"></div>
                  </div>
                </div>
              </div>
            </div>
			{% if activity.type=="call" %}
				<label class="left-date-row text-right" 
					title="{{activity.date|e}}">
					<small>
					<b>Note:</b> {{ activity.date|date("M jS") }} 
					</small>
					<br>
					<small>
					<b>Activity:</b> 
					{{ activity.meta.date|date("M jS")|e }} 
					</small>
				</label>
			{% elseif activity.type=="visit" %}
				<label class="left-date-row text-right" 
					title="{{activity.date|e}}">
					<small>
					<b>Note:</b> {{ activity.date|date("M jS") }} 
					</small>
					<br>
					<small>
					<b>Activity:</b> 
					{{ activity.meta.date|date("M jS")|e }} 
					</small>
				</label>
			{% elseif activity.type=="reminder" %}
				<label class="left-date-row text-right" 
					title="{{activity.date|e}}">
					<small>
					<b>Note:</b> {{ activity.date|date("M jS") }} 
					</small>
					<br>
					<small>
					<b>Activity:</b> 
					{{ activity.meta.date|date("M jS")|e }} 
					</small>
				</label>
			{% else %}
				<!-- this is what's being displayed for anytyhing other than a call, a note or a reminder -->
				<label class="left-date-row text-right" 
					title="{{activity.date}}">
					<small><b>Action:</b> 
					{{ activity.date|date("M jS") }} 
					</small>
				</label>
			{% endif %}
          </div>
        {% endfor %}
      {% endfor %}
    </div>
  </div>
</div>

Look at line #79. You'll notice "note-link." This is giving the user the opportunity to edit the note they're looking at. It's the "pencil" - what the black arrow is pointing to in the above image.

When you click on that, you're activating a pop up window with this code:

 
//you're using this when you want to edit a note
	$(document).on('click', '.note-link', function () {
		var activityId=$(this).attr('data-activityId');
		var dashboard = $(this).attr('data-dashboard');
		var id = (typeof $(this).attr('data-company-id') != 'undefined') ? $(this).attr('data-company-id') : $(this).attr('data-prospect-id');
		var cId = id, 
		pId = $(this).attr('data-proposal-id'),
		type = $(this).attr('data-activity-type');
		activityId = $(this).attr('data-activityId');
		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.displayActivityForm(cId, activityId, dashboard); //this is what's creating your fields
	});

...and the "displayActivityForm" method looks like this:

/* ################################################################### */
/* displayActivityForm with values in fields so you can edit the entry  */
/* ################################################################### */
	
 this.displayActivityForm = function (companyId, activityId, dashboard) {
	$.get('/companies/activities/' +activityId, function(resp) {
		if (resp.error) {
			CAMS.alert('Error', resp.msg, 'error');
			return false;
		}
	let type = resp.type;
	let notes = resp.meta.notes;
	let date = moment(resp.meta.date).format("MM/DD/YYYY h:mm:s A");

	var model = objectModels[type],
		fc = model.singular.substring(0, 1),
		config = {
			title: 'Edit a' + ((fc == 'a' || fc == 'e' || fc == 'i' || fc == 'o' || fc == 'u') ? 'n' : '') + ' ' + ucwords(model.singular),
			directionsText: 'Make your changes below and then click, "SAVE."',
			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
				});
				self.$el.find('#formit-notes').val(notes);
				self.$el.find('#formit-date').val(date);
				self.$el.find('select.form-control.displayType').val(type);

			},
			ajax: {
				path: '/companies/update-activity',
				params: {
					company: companyId,
					activityId: activityId
				},
				onComplete: function (resp, self) {
				if (resp.error) {
					self.addMsg(resp.msg);
					return false;
				}
					if(dashboard=="yes") { //slightly different "success" message for the dashboard
						CAMS.alert('You have successfully updated your activity! Please refresh your page to see the changes.');
						return false;
					}
					else {
						$('#notes-'+activityId).text(resp.notes);
						$('#date-'+activityId).text(moment(resp.date).format("MMM Do, YYYY h:mm A"));
						$('#type-'+activityId).text(resp.type);
						CAMS.alert('You have successfully updated your activity!');
						return false
					}
				}
			}
		};
	// show the form
	formIt.show(config);
	});
};

It's using "formit.js" to create a form and it looks like this:

popup
and just for grins, here's the formit.js code:

'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]) );
  }

  fields.push (this._createField('_csrf', {type: 'hidden', name: '_csrf', value:
      document
      .querySelector('meta[name="csrf-token"]')
      .getAttribute('content')
    }));

  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 || '',
      classes = field.class || '',
      help = field.help || '',
      input = '',
      value = field.value || '';

  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;

      case(field.type == 'hidden'):
      return '<input type="hidden" name="' + name + '" value="' + value + '" />';
      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]';
  };
}

When I do an update, here's my method:

ajax: {
                        path: '/companies/update-activity',
                        params: {
                              company: companyId,
                              activityId: activityId
                        },
                        onComplete: function (resp, self) {
                        if (resp.error) {
                              self.addMsg(resp.msg);
                              return false;
                        }
                              if(dashboard=="yes") { //slightly different "success" message for the dashboard
                                    CAMS.alert('You have successfully updated your activity! Please refresh your page to see the changes.');
                                    return false;
                              }
                              else {
                                    $('#notes-'+activityId).text(resp.notes);
                                    $('#date-'+activityId).text(moment(resp.date).format("MMM Do, YYYY h:mm A"));
                                    $('#type-'+activityId).text(resp.type);
                                    CAMS.alert('You have successfully updated your activity!');
                                    return false
                              }
                        }
                  }

This is part of the "displayActivityForm" code I showed a moment ago.

How can I complete the update, close the popup and update "history-edit.html.twig?"

It's like the parent of a parent.

What do you think?

Thanks!
history.png
Join the community to see this answer!
Join our exclusive community to see this answer & millions of others.
Unlock 1 Answer and 5 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 5 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