admin管理员组文章数量:1025754
I have a custom Gutenberg block which is attempting to take an array of strings and save them as <li>
elements. Everything works as expected and displays correctly to the end user, but I'm getting validation errors after reloading the editor.
Here is the validation error:
Expected:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab"><li class="tab">Tab1</li></li><li class="tab"><li class="tab">Tab2</li></li></ul><p class="custom-content"></p></div>
Actual:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab">Tab1</li><li class="tab">Tab2</li></ul><p class="custom-content"></p></div>
Here is my save function:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab}</li>;
})}
</ul>
</div>
);
}
Here is how I'm parsing the attributes:
attributes: {
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
}
Obviously I'm parsing out the entire HTML list elements, when I only want the text. But when I change the selector to be ul.tabs > li
, I get only the text for a single element, and lists of more than 1 item fail validation.
Can someone help me understand how to get an array of text values?
I have a custom Gutenberg block which is attempting to take an array of strings and save them as <li>
elements. Everything works as expected and displays correctly to the end user, but I'm getting validation errors after reloading the editor.
Here is the validation error:
Expected:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab"><li class="tab">Tab1</li></li><li class="tab"><li class="tab">Tab2</li></li></ul><p class="custom-content"></p></div>
Actual:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab">Tab1</li><li class="tab">Tab2</li></ul><p class="custom-content"></p></div>
Here is my save function:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab}</li>;
})}
</ul>
</div>
);
}
Here is how I'm parsing the attributes:
attributes: {
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
}
Obviously I'm parsing out the entire HTML list elements, when I only want the text. But when I change the selector to be ul.tabs > li
, I get only the text for a single element, and lists of more than 1 item fail validation.
Can someone help me understand how to get an array of text values?
Share Improve this question asked Mar 30, 2019 at 16:47 Sam SchneiderSam Schneider 2483 silver badges10 bronze badges2 Answers
Reset to default 3I found a workaround, sparked by @tmdesigned's answer, which I'm going to post as an answer. It is not pretty and I don't think it can be the "canonical" answer, but hopefully it helps us get there.
So first, I kept the attributes using a source: children
:
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
This meant that on initial save, I was dealing with an array of strings, but on subsequent loads in the editor, I was dealing with an array of objects, since my source type of children was pulling in the <li>
elements themselves in the list, instead of the text values of the elements.
My solution was to map over these elements and convert them back into an array of strings. This had to be done in both the edit
function and the save
function.
const tabValues = tabs.map(tab => {
if (typeof(tab) === 'object')
return tab.props.children[0];
return tab;
});
Then later in the output, I was able to output like this:
{tabValues.map(tab => <li key={tab} className="tab">{tab}</li>)}
If anyone has a solution that relies exclusively on the data parse and doesn't require this workaround, I will mark that as the answer.
When you set the attribute's source to children, it gives you DOM elements, i.e. including the wrapping HTML and not just the inner text.
So the behavior you're seeing, which you probably already know, is the HTML elements getting nested inside of themselves. You're wrapping the contents in <li>
, which gets wrapped in <li>
, which could get wrapped in <li>
again, and so on.
That part I'm pretty sure about. The solution below is my understanding of how to do it, but having not yet done this type of attribute selector, I haven't fully tested this. That being said, I think what you are looking for is closer to:
attributes: {
tabs: {
type: 'array',
source: 'query',
selector: 'ul.tabs',
default: [],
query: {
val: {
type: 'string',
selector: 'li',
source: 'text',
},
}
}
So we're creating an array of objects with one property, which comes from each children's text. Then in your save function, you have to specifically pull that new val
property we just specified as coming from the inner text of the li
selector:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab.val}</li>;
})}
</ul>
</div>
);
}
I have a custom Gutenberg block which is attempting to take an array of strings and save them as <li>
elements. Everything works as expected and displays correctly to the end user, but I'm getting validation errors after reloading the editor.
Here is the validation error:
Expected:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab"><li class="tab">Tab1</li></li><li class="tab"><li class="tab">Tab2</li></li></ul><p class="custom-content"></p></div>
Actual:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab">Tab1</li><li class="tab">Tab2</li></ul><p class="custom-content"></p></div>
Here is my save function:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab}</li>;
})}
</ul>
</div>
);
}
Here is how I'm parsing the attributes:
attributes: {
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
}
Obviously I'm parsing out the entire HTML list elements, when I only want the text. But when I change the selector to be ul.tabs > li
, I get only the text for a single element, and lists of more than 1 item fail validation.
Can someone help me understand how to get an array of text values?
I have a custom Gutenberg block which is attempting to take an array of strings and save them as <li>
elements. Everything works as expected and displays correctly to the end user, but I'm getting validation errors after reloading the editor.
Here is the validation error:
Expected:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab"><li class="tab">Tab1</li></li><li class="tab"><li class="tab">Tab2</li></li></ul><p class="custom-content"></p></div>
Actual:
<div class="wp-block-ggcn-blocks-query-string-content"><span class="data-drop" newtab=""></span><ul class="tabs"><li class="tab">Tab1</li><li class="tab">Tab2</li></ul><p class="custom-content"></p></div>
Here is my save function:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab}</li>;
})}
</ul>
</div>
);
}
Here is how I'm parsing the attributes:
attributes: {
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
}
Obviously I'm parsing out the entire HTML list elements, when I only want the text. But when I change the selector to be ul.tabs > li
, I get only the text for a single element, and lists of more than 1 item fail validation.
Can someone help me understand how to get an array of text values?
Share Improve this question asked Mar 30, 2019 at 16:47 Sam SchneiderSam Schneider 2483 silver badges10 bronze badges2 Answers
Reset to default 3I found a workaround, sparked by @tmdesigned's answer, which I'm going to post as an answer. It is not pretty and I don't think it can be the "canonical" answer, but hopefully it helps us get there.
So first, I kept the attributes using a source: children
:
tabs: {
type: 'array',
source: 'children',
selector: 'ul.tabs',
default: []
}
This meant that on initial save, I was dealing with an array of strings, but on subsequent loads in the editor, I was dealing with an array of objects, since my source type of children was pulling in the <li>
elements themselves in the list, instead of the text values of the elements.
My solution was to map over these elements and convert them back into an array of strings. This had to be done in both the edit
function and the save
function.
const tabValues = tabs.map(tab => {
if (typeof(tab) === 'object')
return tab.props.children[0];
return tab;
});
Then later in the output, I was able to output like this:
{tabValues.map(tab => <li key={tab} className="tab">{tab}</li>)}
If anyone has a solution that relies exclusively on the data parse and doesn't require this workaround, I will mark that as the answer.
When you set the attribute's source to children, it gives you DOM elements, i.e. including the wrapping HTML and not just the inner text.
So the behavior you're seeing, which you probably already know, is the HTML elements getting nested inside of themselves. You're wrapping the contents in <li>
, which gets wrapped in <li>
, which could get wrapped in <li>
again, and so on.
That part I'm pretty sure about. The solution below is my understanding of how to do it, but having not yet done this type of attribute selector, I haven't fully tested this. That being said, I think what you are looking for is closer to:
attributes: {
tabs: {
type: 'array',
source: 'query',
selector: 'ul.tabs',
default: [],
query: {
val: {
type: 'string',
selector: 'li',
source: 'text',
},
}
}
So we're creating an array of objects with one property, which comes from each children's text. Then in your save function, you have to specifically pull that new val
property we just specified as coming from the inner text of the li
selector:
save({attributes, className}) {
const { tabs, newTab, content } = attributes;
return (
<div className={className}>
<span className="data-drop" newtab={newTab}></span>
<ul className="tabs">
{tabs.map(tab => {
return <li className="tab">{tab.val}</li>;
})}
</ul>
</div>
);
}
本文标签: plugin developmentHow to Parse an Array of Elements in Gutenberg Block
版权声明:本文标题:plugin development - How to Parse an Array of Elements in Gutenberg Block 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745640064a2160691.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论