Where should you call model.get() for Form Data

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • BonfireAtNight
    Junior Member
    • Apr 2025
    • 3

    Where should you call model.get() for Form Data

    I'm currently working on a custom field type, more specifically on the template content associated with it. Maybe it's best to first show you what I have, what I get, and what I expected to see instead.

    custom/Espo/Custom/Resources/metadata/fields/contactDetails.json
    Code:
    {
        "params": [],
        "view": "custom:views/fields/contact-details"
    }
    client/custom/src/views/fields/contact-details.js
    Code:
    define(['views/fields/base'], (BaseFieldView) => {
        return class extends BaseFieldView {
            type = 'contactDetails';
            editTemplateContent = `
                <div>
                    <input type="text" class="form-control" data-field="contact-name" value="{{name}}" placeholder="{{contactNamePlaceholder}}" /><br>
                    <input type="text" class="form-control" data-field="contact-phone" value="{{phone}}" placeholder="{{contactPhonePlaceholder}}" /><br>
                    <input type="email" class="form-control" data-field="contact-email" value="{{email}}" placeholder="{{contactEmailPlaceholer}}" />
                </div>
            `;
            detailTemplateContent = `
                <div>
                    <strong>Name:</strong> {{name}}<br>
                    <strong>Phone:</strong> <a href="tel:{{phone}}">{{phone}}</a><br>
                    <strong>Email:</strong> <a href="mailto:{{email}}">{{email}}</a>
                </div>
            `;
            listTemplateContent = `
                {{name}} &lt;{{email}}&gt; {{phone}}
            `;
            data() {
                const formData = this.formData || { name: '', phone: '', email: '' };
          
                return {
                    ...formData,
                    contactNamePlaceholder: this.placeholderName,
                    contactPhonePlaceholder: this.placeholderPhone,
                    contactEmailPlaceholer: this.placeholderEmail,
                };
            }
            setup() {
                // Calling the parent `setup` method, can be omitted.
                super.setup();
                const raw = this.model.get(this.name) || '{}';
                console.log(raw);
                try {
                    const parsed = JSON.parse(raw);
                    this.formData = {
                        name: parsed.name || '',
                        phone: parsed.phone || '',
                        email: parsed.email || ''
                    };
                } catch (e) {
                    this.formData = { name: '', phone: '', email: '' };
                }
          
                // Add translations to be used in the template
                this.placeholderName = this.translate('contactDetailsName', "labels", "Fields");
                this.placeholderPhone = this.translate('contactDetailsPhone', "labels", "Fields");
                this.placeholderEmail = this.translate('contactDetailsEmail', "labels", "Fields");
            }        
    
            fetch() {
                const contactData = {
                    name: this.$el.find('[data-field="contact-name"]').val().trim(),
                    phone: this.$el.find('[data-field="contact-phone"]').val().trim(),
                    email: this.$el.find('[data-field="contact-email"]').val().trim()
                };
                return {
                    [this.name]: JSON.stringify(contactData)
                };
            }
        };
    });
    ​
    What I see:
    • When I create a field with this type and add it to the entity layout, it does show with the expected placeholders. So, this part of the template data works fine.
    • When I save the form, the information on the detail page is shown correctly. So, here the data values for name, phone, and email work fine.
    • When I refresh the page and I click on the newly created entry, THE FIRST TIME the template data for name, phone, and email are not available.
    • When I go back and click on the newly created entry again, it works just fine.
    The problem seems to be with this line:
    const raw = this.model.get(this.name) || '{}';

    The data doesn't seem to be availble in some cases, so that raw is initiated to {}.

    So I guess my question is: What is the best place to call model.get()?
  • yuri
    Member
    • Mar 2014
    • 8860

    #2
    When you go from a list view to the detail view, the view is rendered without based on the model passed from the list view. It makes it possible to render the detail view instantly.

    Then, the model is full fetched and fields that were without data are re-rendered.

    Not-set fields are supposed to be rendered with a '–' character indicating that the data is being loading.

    The setup method is called only once. It's not called when the data is fetched later.

    There are multiple strategies how it can be handled.

    You can add an event listened in the setup method.

    this.listenTo(this.model, 'sync', () => {});

    Or you can full rely on the model data avoiding writing data to somewhere else.
    If you find EspoCRM good, we would greatly appreciate if you could give the project a star on GitHub. We believe our work truly deserves more recognition. Thanks.

    Comment

    • BonfireAtNight
      Junior Member
      • Apr 2025
      • 3

      #3
      Thanks for your answer!

      This is working now:
      Code:
      setup() {
                  super.setup();
            
                  // Set placeholder translations
                  this.placeholderName = this.translate('contactDetailsName', "labels", "Fields");
                  this.placeholderPhone = this.translate('contactDetailsPhone', "labels", "Fields");
                  this.placeholderEmail = this.translate('contactDetailsEmail', "labels", "Fields");
            
                  this.updateFormData();
            
                  this.listenTo(this.model, 'sync', () => {
                      this.updateFormData();
                  });
              }
            
              updateFormData() {
                  const raw = this.model.get(this.name) || '{}';
                  try {
                      const parsed = JSON.parse(raw);
                      this.formData = {
                          name: parsed.name || '',
                          phone: parsed.phone || '',
                          email: parsed.email || ''
                      };
                  } catch (e) {
                      this.formData = { name: '', phone: '', email: '' };
                  }
              }
      Is this what you had in mind? Or is there a reason why I shouldn't do it like this?

      Comment

      Working...