Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block API: Allow for internal, non-duplicable block attributes #29693

Open
stacimc opened this issue Mar 9, 2021 · 16 comments · May be fixed by #34750 or #38643
Open

Block API: Allow for internal, non-duplicable block attributes #29693

stacimc opened this issue Mar 9, 2021 · 16 comments · May be fixed by #34750 or #38643
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Blocks Overall functionality of blocks [Type] New API New API to be used by plugin developers or package users.

Comments

@stacimc
Copy link
Contributor

stacimc commented Mar 9, 2021

What problem does this address?

Blocks cannot use attributes to store information that is unique internal to the block instance but also persisted across page loads. Here's an illustrative example:

I'm working on the Jetpack Pay with PayPal block. It contains a productId attribute which uniquely identifies the actual product record referenced by the block. When a new block is inserted this attribute will be undefined; we hit the API to create a new product, and store the returned id in this attribute.

When the block is duplicated, I'd like to create a new product record with identical fields (title/price/etc). But because the productId is cloned along with all the other attributes, both blocks will point to the same resource. It's possible to keep track of the block's unique clientId and reset the product when this value changes — but while this detects block duplication, it also changes every time the post loads, and I only want to reset the product on duplication.

What is your proposed solution?

A couple of options as a starting point for discussion:

  1. Add support for unique internal attributes, which do not get copied during block duplication.
  2. Allow blocks to listen for/handle the duplication event.
@Tropicalista
Copy link

I have similar problem.

Here's my issue: #29694

I'm creating a form builder. I need a way to store reference to form id in database. When a block is made reusable it stores the reference, but if a user convert to normal block I need a way to detach the old reference in DB and create a new one.

I've just discovered that there's a new __experimentalIsEditingReusableBlock that probably can suite for my use case.

@talldan
Copy link
Contributor

talldan commented Mar 10, 2021

Also related is the difficulty in declaring and using unique HTML ids within a block - #17246

@ntsekouras
Copy link
Contributor

I've been thinking about this and a possible solution I had in mind to try out involves these:

  1. Set the unique attribute value on load only if it doesn't exist and mark this change as persistent or not depending on your needs (add undo level). Something like this: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/query/edit/index.js#L94
  2. Use the __experimentalRole attribute to something like id or unique etc.., and then use the existing __experimentalGetBlockAttributesNamesByRole util to get them.
  3. Handle them in the appropriate places, like in the duplicate function and where is needed.

All the above are using experimental APIs though which were introduced here: #30469. I guess this could be okay though as the new functionality should also be experimental for start.

@talldan
Copy link
Contributor

talldan commented Jun 14, 2021

This could also be relevant for the widget editors, which store an internal widgetId as an attribute. (cc @kevin940726 @noisysocks).

@skorasaurus skorasaurus added the [Type] Enhancement A suggestion for improvement. label Jun 14, 2021
@gziolo
Copy link
Member

gziolo commented Sep 8, 2021

This could also be relevant for the widget editors, which store an internal widgetId as an attribute. (cc @kevin940726 @noisysocks).

The related PR #28379. We discussed the potential new APIs for block attributes with @spacedmonkey on Twitter. Related thread:

https://twitter.com/thespacedmonkey/status/1435530281595846663

Well this is a question. I think they could be two kind of attribute. Internal and private/sensitive. Internal attributes, could used for data that is not sensitive but not design to be public. Private / sensitive, like api keys, personal information (like email) or passwords.

The conclusion in the discussion is that "internal" attribute is more something stored in the memory during the block editor's runtime and __internalWidgetId from the linked PR would be a good example. Here, @stacimc desribes the use case where an attribute (productId) should be persisted between the page edits but doesn't necessarily need to be secret. So I have a feeling that it's something between the shared definition of internal and private/sensitive 😄 Well, unless we introduce three types:

  • internal - in memory only, not persisted in HTML, not shared when duplicating
  • private - persisted in the HTML, not shared when duplicating
  • secret - persisted in secure location, not shared when duplicating

@gziolo gziolo changed the title Allow for persistent unique block attributes Block API: Allow for persistent unique block attributes Sep 8, 2021
@gziolo gziolo added [Feature] Block API API that allows to express the block paradigm. [Type] New API New API to be used by plugin developers or package users. [Status] In Progress Tracking issues with work in progress and removed [Type] Enhancement A suggestion for improvement. labels Sep 8, 2021
@noisysocks
Copy link
Member

  • secret - persisted in secure location, not shared when duplicating

Not sure I understand this. What would a "secure location" be?

@gziolo
Copy link
Member

gziolo commented Sep 9, 2021

  • secret - persisted in a secure location, not shared when duplicating

Not sure I understand this. What would a "secure location" be?

I don’t know yet, too 😅 I understood that the value couldn’t be serialized to content as HTML, so it isn’t easily exposed. I also assume it should live somewhere in the database. I was trying to document potential options and their subtle differences.

@stacimc
Copy link
Contributor Author

stacimc commented Sep 10, 2021

I've just opened #34750 borrowing the internal terminology from @gziolo 😄

I think this issue may be trying to do too many things, largely because of my use of the loaded term unique. To solve my own original use case, it's sufficient to have a way to prevent an attribute from being copied on block duplication. internal feels like a superior descriptor.

Per @ntsekouras I like internal as an __experimentalRole for an attribute, rather than a boolean property. I'm interpreting the internal role strictly as: "attribute does not get copied on block duplication", rather than adding any additional constraints of uniqueness/etc.

I think this would go a long way to addressing many of the use cases described here, excluding the private or secret ideas. I'd like to edit the title and description of this issue slightly to replace the loaded unique terminology, and perhaps open a separate issue to track the desire for private/secure attributes, if that makes sense to others here. What do you think?

@gziolo
Copy link
Member

gziolo commented Sep 13, 2021

I've just opened #34750 borrowing the internal terminology from @gziolo 😄

Thank you for a quick follow-up 👍🏻

I think this would go a long way to addressing many of the use cases described here, excluding the private or secret ideas. I'd like to edit the title and description of this issue slightly to replace the loaded unique terminology, and perhaps open a separate issue to track the desire for private/secure attributes, if that makes sense to others here. What do you think?

Feel free to edit the issue title and description according to the current direction of the PR you opened. It sounds like a good idea to open another issue to continue the discussion for other common scenarios that block authors have to deal with.

@Tropicalista
Copy link

Any update on this?

@albanyacademy
Copy link

Does copy+paste count as duplication event?

@noisysocks
Copy link
Member

Some other use cases for an API such as this were discussed in #23377.

@anver
Copy link
Contributor

anver commented Jun 19, 2024

Any solution to this issue ?

I’m creating a custom block and one of it’s attribute is a unique id which doesn’t have any UI, so that’s not editable by the user. It’s created automatically by useEffect hook on the component startup if that attribute is not set. If I duplicate the block then the attribute is copied too which makes a duplicate id. Is there any way so that I can detect if a block is being duplicated and generate a unique id and overwrite the duplicate id with the generated one programmatically ?

@ntsekouras
Copy link
Contributor

Any solution to this issue ?

Unfortunately not yet. Your use case is exactly the same with Query Loop block and queryId.

@acketon
Copy link

acketon commented Jun 20, 2024

@anver I had a similar problem for our Accordian block. We needed to generate unique ID's for use on the frontend. These were needed for aria labels and some frontend JS, etc. I put together a work around that appears to work. It's hacky but I think it will suffice for our needs for now. It's based on some great discussion here in a few issues and on what was done in the core heading block for generating anchors.

Basically uses useEffect() combined with searching all the blocks in the editor to check for a duplicate id stored as a data attribute. If a duplicate is found it generates a new unique ID. That seems so far to work when a block is duplicated or copy/pasted, etc. bu-ist/bu-blocks#355.

Example:
bu-ist/bu-blocks#356

@anver
Copy link
Contributor

anver commented Jun 20, 2024

@anver I had a similar problem for our Accordian block. We needed to generate unique ID's for use on the frontend. These were needed for aria labels and some frontend JS, etc. I put together a work around that appears to work. It's hacky but I think it will suffice for our needs for now. It's based on some great discussion here in a few issues and on what was done in the core heading block for generating anchors.

Basically uses useEffect() combined with searching all the blocks in the editor to check for a duplicate id stored as a data attribute. If a duplicate is found it generates a new unique ID. That seems so far to work when a block is duplicated or copy/pasted, etc. bu-ist/bu-blocks#355.

Example: bu-ist/bu-blocks#356

@acketon Thanks for the right direction, i had to change few things on that code to make it work, but i had to ditch that completely and go for a reliable solution, finally I found a solution :) Thx for the comments bro.

const getBlockCount = () => select( 'core/block-editor' ).getGlobalBlockCount();
let blockCount = getBlockCount();

	useEffect( () => {
		const unsub = subscribe( () => {
			const newCount = getBlockCount();

			if ( newCount === blockCount ) return;

			blockCount = newCount;

			const wrapper = select( 'core/block-editor' ).getBlock( clientId )!;

			const formBlocks = wrapper.innerBlocks.filter(
				( block ) => block.name === 'surveyboss/form'
			);

			const duplicates = formBlocks.filter( ( obj, index, arr ) =>
				arr.find(
					( innerObj ) =>
						innerObj.attributes.formUuid ===
							obj.attributes.formUuid &&
						innerObj.clientId !== obj.clientId
				)
			);

			if ( duplicates.length <= 1 ) return;

			dispatch( 'core/block-editor' ).updateBlockAttributes(
				duplicates[ 1 ].clientId,
				{ formUuid: generateId() }
			);

			dispatch( 'core/block-editor' ).updateBlockAttributes(
				duplicates[ 1 ].clientId,
				{
					metadata: {
						...duplicates[ 1 ].attributes.metadata,
						name: 'Untitled Form',
					},
				}
			);
		} );

		return unsub;
	}, [ clientId ] );

This is the part of the code i implemented and it just works fine. You got to put the effect in the parent of the duplicate components you are working with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Blocks Overall functionality of blocks [Type] New API New API to be used by plugin developers or package users.
Projects
None yet
10 participants