From b7c1c4b92beb40e99cd58e4f4a7f0b072313754e Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Fri, 9 Jan 2015 17:41:13 +0000 Subject: [PATCH] Updated StreamField blocks API (markdown) --- StreamField-blocks-API.md | 46 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/StreamField-blocks-API.md b/StreamField-blocks-API.md index 582382a..b1e38bb 100644 --- a/StreamField-blocks-API.md +++ b/StreamField-blocks-API.md @@ -20,25 +20,15 @@ A 'block definition' object roughly corresponds to a 'widget' in Django's forms (This is a page consisting of a StreamField where the only block type that's available to be inserted is a shopping list.) -The constructor can take whatever parameters it likes, but the base `Block` class handles the parameters 'default' and 'label' as standard. Block objects should be considered immutable once they're created, with one exception: the parent block may assign it an internal name (`'shopping_list'` in this example) for its own purposes by calling `set_name(name)` on the block. This is not essential, and blocks must still work correctly without a name; for example, in the construction `ListBlock(ShoppingListBlock(classname='polkadot'))` (a list of shopping lists), ShoppingListBlock is not given a name. As far as the block itself is concerned, the name is typically only used as a fallback label for when an explicit 'label' parameter has not been supplied. (Again, the base `Block` class takes care of this.) +The constructor can take whatever parameters it likes, but the base `Block` class handles the parameters 'default' and 'label' as standard. Block definition objects should be considered immutable once they're created, with one exception: the parent block may assign it an internal name (`'shopping_list'` in this example) for its own purposes by calling `set_name(name)` on the block. This is not essential, and blocks must still work correctly without a name; for example, in the construction `ListBlock(ShoppingListBlock(classname='polkadot'))` (a list of shopping lists), the ShoppingListBlock is not given a name. As far as the block itself is concerned, the name is typically only used as a fallback label for when an explicit 'label' parameter has not been supplied. (Again, the base `Block` class takes care of this.) ## The block API -Block factory objects need to provide the following attributes/methods: +Subclasses of `Block` must implement the following methods: -### media +### render_form(self, value, prefix='') -A [Django media object](https://docs.djangoproject.com/en/1.7/topics/forms/media/#media-objects) specifying any external static JS/CSS files required by this block definition. The actual low-level Javascript implementation (such as making an 'add new' button that spawns a new "Product" text field when clicked) would usually be written here - in a file called 'shopping_list.js', say. - -### html_declaration(self) - -Returns a (possibly empty) string of HTML. This HTML will be included on the edit page - ONCE per block definition, regardless of how many occurrences of the block there are on the page - and it will appear in a non-specific place on the page. Any element IDs within this block of HTML must be prefixed by the factory's definition_prefix. - -Typically this will be used to define snippets of HTML within `` blocks, for our Javascript code to work with. For example, our shopping list block type might define this snippet to be dynamically inserted when you click the 'add new' button: - - +Return the HTML for an instance of this block with the content given by 'value'. All element IDs and names must be prefixed by the given prefix. (The calling code will ensure that this prefix is unique for each individual block instance that appears in the form, in the case of repeatable blocks.) > **A short aside about ID prefixing:** > @@ -50,10 +40,9 @@ Typically this will be used to define snippets of HTML within `` blocks, for our Javascript code to work with. For example, our shopping list block type might define this snippet to be dynamically inserted when you click the 'add new' button: + + + + ### js_declaration(self) Returns a Javascript expression string, or None if this block does not require any Javascript behaviour. This expression will be evaluated ONCE per block definition, regardless of how many occurrences of the block there are on the page. This expression evaluates to a "meta-initializer function". @@ -208,4 +218,4 @@ The default value for blocks of this type; used as the initial value for newly-c Implementation details that should be covered here (but aren't totally set in stone yet): * Javascript API refactor - rather than two levels of indirection (meta-initialiser and initialiser), there's just an initialiser function that takes js_declaration_param and prefix as its two parameters -* BoundBlock as a helper; ~~calls to js_declaration_param should be done as block_factory.bind(val, prefix).js_declaration_param() rather than block_factory.js_declaration_param(val) so that blocks can potentially subclass BoundBlock to carry extra data around~~ (not yet; it means recursive/structural blocks are forced into that pattern too, in order to be able to pass prefix to child_block_factory.bind()) +* BoundBlock as a helper; ~~calls to js_declaration_param should be done as block_factory.bind(val, prefix).js_declaration_param() rather than block_factory.js_declaration_param(val) so that blocks can potentially subclass BoundBlock to carry extra data around~~ (not yet; it means recursive/structural blocks are forced into that pattern too, in order to be able to pass prefix to child_block_factory.bind()) \ No newline at end of file