Wagtail:如何定义自定义块的 js_initializer,以便子块的 js_initializer 也可以 运行?

Wagtail: How does one define a custom Block's js_initializer so that subblocks' js_initializers also run?

我最初是在 wagtail 问题队列上问这个问题的,我猜这是错误的地方。 (尽管我确实认为这是 documentation 中的错误。)

无论如何,我的问题是我有一个自定义 StructBlock class,其中使用了 ListBlock。我需要在我的自定义 class 上定义一个 js_initializer() 方法,它会导致表单触发我自己的初始化程序 ListBlock 的初始化程序。

我最初的尝试,基于 docs,看起来像这样:

# my_blocks.py
class ImageGalleryBlock(blocks.StructBlock):
    images = ListBlock(ImageChooserBlock(label='Image'))

    def js_initializer(self):
        return "ImageGallery"

    @property
    def media(self):
        return forms.Media(
            js=['app/js/admin/image-gallery.js']
        )


# image-gallery.js
function ImageGallery(prefix) {
    // Set up the Image Gallery block's custom form behavior...
}

这使 ImageGallery() 函数成为 运行,但没有 运行 ListBlock 的初始化程序,因此 none 的按钮起作用了。

在 wagtail 问题队列中,有人建议我尝试这样的操作:

def js_initializer(self):
    initializer_js = super(HeadingBlock, self).js_initializer()
    my_custom_js = 'ImageGallery("%s")' % self.definition_prefix
    if initializer_js:
        # child blocks have custom JS initializers and need to be used
        return '%s,\n%s' % (initializer_js, my_custom_js)
    return my_custom_js

# image-gallery.js
function ImageGallery(prefix) {
  var init_image_gallery = function(element_prefix) {
    // Do stuff...
  };

  return init_image_gallery;
}

我不得不对最初的建议进行一些改进以使 ImageGallery() 部分起作用,但它仍然没有 运行 ListBlock 初始化程序。

下面是为 ImageGalleryBlock 生成的初始化代码:

{
    'name': ('ImageGalleryBlock'),
    'initializer': (StructBlock({
        'images': (ListBlock({
            'definitionPrefix': ('blockdef-63')
        }))
    }),
    ImageGallery("blockdef-91"))
},

我觉得我真正需要做的是向传递给 StructBlock 的字典添加另一个键,但我不知道如何做。

一个js_initializer方法returns一个Javascript表达式,在页面加载时计算一次,并吐出一个函数;每次将块插入表单时都会调用此初始化函数,传递 ID 前缀以标识应接收 Javascript 行为的 HTML 元素。重要的是要理解这是一个两步过程 - 页面加载的初始评估(通常采用函数调用的形式 returns 第二步中要使用的函数),以及调用初始化函数对于表单上的每个块。

每当一个块充当其他块的包装器时,就像 StructBlock 所做的那样,它负责确保对其子块执行此合同:当它自己的 js_initializer 在页面加载时被评估时,子块js_initializers 也需要在那个时候被评估,当它的初始化函数被调用时,它会调用那些子初始化函数。

通过子类化 StructBlock 并覆盖 js_initializer,您实际上是在 StructBlock 周围添加了另一层包装:您的新 js_initializer 需要评估为一个函数,该函数既调用 StructBlock 的初始化函数,又执行您的自定义图片库设置。方法如下:

图片-gallery.js:

/* ImageGallery gets called once on startup; the function it returns will
be called whenever we need to set up an image gallery block on the form */
function ImageGallery(parentInitializer) {
    return function(elementPrefix) {
        /* call the original StructBlock initializer */
        parentInitializer(elementPrefix);

        /* do whatever JS setup you need for the image gallery behaviour */
        $('#' + elementPrefix + '-gallery').doStuff();
    };
}

my_blocks.py:

class ImageGalleryBlock(blocks.StructBlock):
    images = ListBlock(ImageChooserBlock(label='Image'))

    def js_initializer(self):
        parent_initializer = super(ImageGalleryBlock, self).js_initializer()
        return "ImageGallery(%s)" % parent_initializer

    @property
    def media(self):
        # need to pull in StructBlock's own js code as well as our own
        return super(ImageGalleryBlock, self).media + forms.Media(
            js=['app/js/admin/image-gallery.js']
        )