Трясогузка: Как определить js_initializer пользовательского блока, чтобы js_initializers подблоков также запускались?

Первоначально я задал этот вопрос в очереди вопросов о трясогузках, и я думаю, что это было неподходящее место для него. (Хотя я думаю, что это ошибка в документации. .)

Во всяком случае, моя проблема в том, что у меня есть собственный класс StructBlock, который использует внутри него ListBlock. Мне нужно определить метод js_initializer() в моем пользовательском классе, который заставляет форму запускать как мой собственный инициализатор , так и инициализатор ListBlock.

Моя первоначальная попытка, основанная на документах, выглядела так: :

# 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, поэтому ни одна из его кнопок не работает.

В очереди на выпуск трясогузки мне порекомендовали попробовать что-то вроде этого:

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"))
},

У меня такое ощущение, что на самом деле мне нужно добавить еще один ключ к dict, передаваемому в StructBlock, но я понятия не имею, как это сделать.


person coredumperror    schedule 08.12.2017    source источник
comment
Пожалуйста, включите минимальный, полный и проверяемый пример кода - вы не предоставили достаточно общего доступа к ImageGalleryBlock, чтобы увидеть, где задействованы StructBlock и ListBlock.   -  person gasman    schedule 09.12.2017
comment
@gasman Упс, я немного перепутал определение ImageGalleryBlock. Вы можете сказать, что слишком сильно сократили его. Это должно быть правильно, сейчас.   -  person coredumperror    schedule 09.12.2017


Ответы (1)


Метод js_initializer возвращает выражение Javascript, которое вычисляется один раз при загрузке страницы и выдает функцию; эта функция инициализации затем вызывается каждый раз, когда ваш блок вставляется в форму, передавая префикс идентификатора для идентификации элементов HTML, которые должны получать поведение Javascript. Важно понимать, что это двухэтапный процесс — начальная оценка при загрузке страницы (которая часто принимает форму вызова функции, которая возвращает функцию, которая будет использоваться на втором этапе), и вызов функции инициализации для каждого блока на форма.

Всякий раз, когда блок выступает в качестве оболочки для других блоков, как это делает StructBlock, он отвечает за обеспечение соблюдения этого контракта для своих дочерних блоков: когда его собственный js_initializer оценивается при загрузке страницы, дочерние js_initializer также должны быть оценены в этот момент. , и когда вызывается его функция инициализатора, он вызывает эти дочерние функции инициализатора.

Создавая подклассы StructBlock и переопределяя js_initializer, вы фактически добавляете еще один слой оболочки вокруг StructBlock: ваш новый js_initializer должен оценивать единую функцию, которая одновременно вызывает функцию инициализации StructBlock и выполняет настройку пользовательской галереи изображений. Вот как это сделать:

изображение-галерея.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']
        )
person gasman    schedule 10.12.2017
comment
Вот это отличное объяснение! Наконец-то все работает! Хотя мне нужно было заменить вторую букву s, которую вы использовали в js_initialiser(), на z, так как эта функция пишется по-американски. Думаю, было бы неплохо добавить это в документы. - person coredumperror; 11.12.2017
comment
Этого до сих пор нет в документах, и это действительно, действительно должно быть. Я только что провел еще час, ломая голову над тем, почему ListBlock в одном из моих новых пользовательских блоков не работал должным образом, потому что я забыл об этой проблеме с js_initializer. - person coredumperror; 15.05.2019