Announcement

Collapse
No announcement yet.

Editable/Updatable Fields in Lists

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Editable/Updatable Fields in Lists

    It would be super useful to be able to click on a field empty or data in a list and be able to update it inline. It was one of the most useful features of KarmaCRM (if you want to see how they did it).

    Really impressed with the approach in espo though - VERY powerful and cool.

  • #2
    I feel a need for this. Would be great

    Comment


    • #3
      I also think that this would be a great feature, Espo is incredibly powerful and cool.

      If this is not in the future plans, could you provide some guidelines for someone else to do a custom implementation ?

      Comment


      • #4
        We need this too.

        Comment


        • #5
          Unfortunately, not planned.

          Comment


          • #6
            Would it be possible to add an action button to each row/record in a list view ? and if so could you tell me how to go about it ?

            Comment


            • #7
              I managed to implement the existing inline edit functionality to fields in list display. Here are the steps and code for those interested:

              Step 1: Create a new field base view with generic inline edit functions extending the existing "views/fields/base" view:
              Code:
              Espo.define('custom:views/fields/inline-base', 'views/fields/base', function (Dep) {
              
                  return Dep.extend({
              
                      customInitInlineEdit: function () { // substitutes initInLineEdit() at base.js
                          var $targetEditEl = $('tr[data-id="'+this.model.id+'"] > td[data-name="'+this.name+'"] > a.inline-edit-link');
                          // if the edit link already exists, avoid duplicanting
                          if($targetEditEl.length != 0) {
                              return;
                          }            
                          var $targetCell = $('tr[data-id="'+this.model.id+'"] > td[data-name="'+this.name+'"]');
                          var $editLink = $('<a href="javascript:" class="pull-right inline-edit-link hidden"><span class="fas fa-pencil-alt fa-sm"></span></a>');
                          if ($targetCell.length == 0) {
                              // if the target cell has not been rendered, wait until it has been rendered to perform the function customInitInlineEdit
                              this.listenToOnce(this, 'after:render', this.customInitInlineEdit, this);
                              return;
                          }
                          $targetCell.prepend($editLink);
                          $editLink.on('click', function () {
                              this.customInlineEdit();
                          }.bind(this));
                          $targetCell.on('mouseenter', function (e) {
                              e.stopPropagation();
                              if (this.disabled || this.readOnly) {
                                  return;
                              }
                              $editLink.removeClass('hidden');
                          }.bind(this)).on('mouseleave', function (e) {
                              e.stopPropagation();
                              $editLink.addClass('hidden');
                          }.bind(this));
                      },
              
                      customInlineEdit: function () { // substitutes inlineEdit() at base.js
                          var self = this;
                          this.trigger('edit', this);
                          this.setMode('edit');
                          this.initialAttributes = this.model.getClonedAttributes();
                          this.once('after:render', function () {
                              this.customAddInlineEditLinks(); // add the edit links "Cancel and Update"
                          }, this);
                          this._isInlineEditMode = true;
                          this.reRender(true);
                          this.trigger('inline-edit-on');
                      },
              
                      customAddInlineEditLinks: function () { // substitutes addInlineEditLinks() at base.js
                          var $targetCell = $('tr[data-id="'+this.model.id+'"] > td[data-name="'+this.name+'"]');
                          var $saveLink = $('<a href="javascript:" class="pull-right inline-save-link">' + this.translate('Update') + '</a>');
                          var $cancelLink = $('<a href="javascript:" class="pull-right inline-cancel-link">' + this.translate('Cancel') + '</a>');
                          $targetCell.prepend($saveLink);
                          $targetCell.prepend($cancelLink);
                          $targetCell.find('.inline-edit-link').addClass('hidden');            
                          $saveLink.click(function () {
                              this.customInlineEditSave();
                          }.bind(this));            
                          $cancelLink.click(function () {
                              this.customInlineEditClose();
                          }.bind(this));
                      },
              
                      customInlineEditSave: function () {
                          var data = this.fetch();
                          var self = this;
                          var model = this.model;
                          var prev = this.initialAttributes;
                          model.set(data, {silent: true});
                          data = model.attributes;
                          var attrs = false;
                          for (var attr in data) {
                              if (_.isEqual(prev[attr], data[attr])) {
                                  continue;
                              }
                              (attrs || (attrs = {}))[attr] =    data[attr];
                          }
                          if (!attrs) {
                              this.customInlineEditClose();
                              return;
                          }
                          if (this.validate()) {
                              this.notify('Not valid', 'error');
                              model.set(prev, {silent: true});
                              return;
                          }
                          this.notify('Saving...');
                          model.save(attrs, {
                              success: function () {
                                  self.trigger('after:save');
                                  model.trigger('after:save');
                                  self.notify('Saved', 'success');
                                  // if a parent model was passed, refresh the parent model which will refresh the parent view as well
                                  if(self.options.parentModel) {
                                      self.options.parentModel.fetch();
                                  }
                              },
                              error: function () {
                                  self.notify('Error occured', 'error');
                                  model.set(prev, {silent: true});
                                  self.render()
                              },
                              patch: true
                          });
                          this.customInlineEditClose(true);
                      },
              
                      customInlineEditClose: function (dontReset) { // substitutes inlineEditClose @ base.js
                          this.trigger('inline-edit-off');
                          this._isInlineEditMode = false;
                          if (this.mode != 'edit') {
                              return;
                          }
                          this.setMode('list');            
                          if (!dontReset) {
                              this.model.set(this.initialAttributes);
                          }
                          this.reRender(true);
                          this.customInitInlineEdit();
                      }
              
                  });
              });
              Step 2: Extend the original field type view (eg: text) to include the list inline edit capability:
              Code:
              Espo.define('custom:views/fields/inline-text', 'custom:views/fields/inline-base', function (Dep) {
              
                  return Dep.extend({
              
                      init: function(){
                          Dep.prototype.init.call(this); // call the parent "init()" function
                          // if the current view mode is 'list' make the field editable after it has been rendered and when the model has been updated
                          if(this.mode==='list') {
                              this.listenToOnce(this, 'after:render', this.customInitInlineEdit(), this);       
                              this.listenTo(this.model, 'after:save', function () {
                                 this.customInitInlineEdit(); 
                              }, this);
                          }
                      },
              
              // FROM HERE ON JUST COPY THE CODE FROM THE ORIGINAL FIELD VIEW: "views/fields/text"
              
                      type: 'text',
                      listTemplate: 'fields/text/list',
                      detailTemplate: 'fields/text/detail',
                      editTemplate: 'fields/text/edit',
                      searchTemplate: 'fields/text/search',
                      seeMoreText: false,
                      rowsDefault: 10,
                      rowsMin: 2,
                      seeMoreDisabled: false,
                      cutHeight: 200,
                      searchTypeList: ['contains', 'startsWith', 'equals', 'endsWith', 'like', 'notContains', 'notLike', 'isEmpty', 'isNotEmpty'],
              ...
              Step 3: Specify the custom field view in entityDefs
              Code:
              {
                  "fields": {
                      "name": {
                          "type": "varchar",
                          "required": true,
                          "trim": true,
                          "audited": false,
                          "readOnly": true,
                          "tooltip": false
                      },
                      "description": {
                          "type": "text",
                          "view": "custom:views/fields/inline-text",
                          "audited": false,
                          "readOnly": false,
                          "isCustom": true
                      },
              ...
              I am not an experienced developer so it's likely that this is not an elegant solution but it's working for me, at least for text and varchar which I needed for our implementation. If anybody has any suggestions for improvement please share. Cheers

              Comment


              • #8
                Hi telecastg ,

                I think we need EXACTLY what you mention. I haven't tested your instructions but it would be VERY useful if you can take some screenshots just to be sure of the result of these modifications.

                Can you PLEASE help us uploading any screenshots?

                Thanks a lot.

                Comment


                • #9
                  Hello Pablo

                  This code is a couple of years old but should still work.

                  I couldn't post a still image because it works with the mouseover event. I recorded a short video from my screen but the forum will not accept local video clips....

                  Anyway, it works the same way that the inline edit feature works in detail view:

                  You move the mouse over the field and a "pencil" icon shows up in the top right corner of the field. You click on the pencil and the field becomes editable. Changes can be saved by clicking on the "Update" link or cancelled by clicking on the "Cancel" link. Either of those actions will return the field to detail display instead of edit display.

                  Comment


                  • #10
                    Thank a LOT telecastg for your effort. I will test it

                    Comment


                    • telecastg
                      telecastg commented
                      Editing a comment
                      You're welcome

                  • #11
                    telecastg

                    Thanks for the awesome guide,
                    I did test your code it's working but there is a problem, when you click inline edit , if you don't cancel the edit and click on a record to go to the detail view when you click back to the browser, inline edit is still active but update and cancel buttons are gone

                    I did try using on after:render instead of listenToOnce but it makes it worse

                    Comment


                    • #12
                      hello goodwill

                      Thanks for bringing this up, I had not noticed, I am very busy at the moment but will look into it.

                      In the meantime, one easy fix is to click on the pencil icon again over the field when you get back to the list display and the "Update" and "Cancel" links will show up.

                      Comment


                      • #13
                        For those that are interested in this feature plus column resizing capability , I just released a new commercial extension, compatible with Espo 7.
                        New commercial extension, compatible with Espo 7.x to give, using the Administration Panel, the ability to edit any entity inline while in list display, and to resize the list columns manually. This extension can be purchased here: https://payhip.com/b/ZGQMH

                        Comment

                        Working...
                        X