withlogicco / django-prose

Wonderful rich-text editing for your Django project
https://stateofprogress.blog/2023/02/01/django-prose-wonderful-rich-text-content-editing-for-django
MIT License
202 stars 13 forks source link

RichTextField crashes with inline formsets | Unfold Admin #100

Closed EvelinaIo closed 3 months ago

EvelinaIo commented 4 months ago

Description

We are experiencing a TypeError in the console when attempting to add an item to a stacked inline in the Django admin. The issue occurs because the model for the inline contains a RichTextField.

Steps to Reproduce

  1. Navigate to the Django admin page for the model that contains the stacked inline.
  2. Click on the button to add a new item to the inline.
  3. Observe the console for the error message.

Expected Behavior

A new inline form should be added without any errors.

Actual Behavior

A TypeError is thrown in the console, and the new inline form is not added.

inlines.js:35 Uncaught TypeError: Cannot set property name of #<Pn> which has only a getter
    at updateElementIndex (inlines.js:35:25)
    at Pn.<anonymous> (inlines.js:73:17)
    at Function.each (jquery.js:383:19)
    at jQuery.fn.init.each (jquery.js:205:17)
    at HTMLAnchorElement.addInlineClickHandler (inlines.js:72:27)
    at HTMLAnchorElement.dispatch (jquery.js:5145:27)
    at elemData.handle (jquery.js:4949:28)

Environment

OS: macOS 14.2 Django Version: 5.0.6 Django Unfold: 0.20.5 Django Prose: 2.0.0

Additional Context

The specific project uses Django unfold admin to override the theme. This issue appears to be related to the integration of the RichTextField within the stacked inline. The problem does not occur with inlines that do not contain a RichTextField.

It seems that the error occurs when the inlines.js script tries to update the name property for trix-editor, probably because it expects an input element, and fails because there is none.

parisk commented 4 months ago

A few insites here. This issue is caused by this particular line in Django^1:

row.find("*").each(function() {
    updateElementIndex(this, options.prefix, totalForms.val());
});

and consequently by attempting to update the name^2 of the returned elements:

if (el.name) {
  el.name = el.name.replace(id_regex, replacement);
}

The .find(...)^3 jQuery function returns child elements across all depths, not just direct ones. As a result, it also returns the trix-editor elements, which are custom elements implemented by TrixEditorElement^4. Now, TrixEditorElement does have indeed a name attribute, which is explicitly set as a getter^5, so inlines.js attempts to change it, which is not possible because TrixEditorElement does not set a setter as well, for this property.

The way I suggest to approach this is go into the initializeEditors function: https://github.com/withlogicco/django-prose/blob/a85149873d44dc62b7a0c4ce41adce07101bd3c6/prose/static/prose/editor.js#L49-L58

and define a setter property^6 for all trix-editor elements that have the django-prose-editor class.

parisk commented 3 months ago

Closed in https://github.com/withlogicco/django-prose/pull/104.