Gutenberg removing table tag on save - wordpress

I am attempting to create a Gutenberg block to track exercise sets. I'm using a RichText component to allow users to edit default values in a table I pre-populate for them.
The block works well on the editor and, after saving, renders correctly in the post. However, when I reload the editor, I receive this error message: Block validation: Expected tag name 'thead', instead saw 'table'. It's almost like Gutenberg is stripping the table tag but leaving everything else.
Of course, that doesn't make sense, but I'm not sure what else it could be.
Here's my code, heavily edited for readability:
const { registerBlockType } = wp.blocks;
const { AlignmentToolbar, BlockAlignmentToolbar, BlockControls, RichText, useInnerBlockProps } = wp.blockEditor;
const { Component } = wp.element;
registerBlockType('bsd-strong-post/training-session', {
title: __('Strong Post', 'bsd-strong-post'),
description: __('Provides a short summary of a training session', 'bsd-strong-post'),
category: 'common',
icon: blockIcons.weight_lifting,
keywords: [
__('strength workout', 'bsd-strong-post'),
__('strong', 'bsd-strong-post'),
__('training', 'bsd-strong-post')
],
supports: {
html: true
},
attributes: {
/* ... */,
dayTemplateContent: {
type: 'string',
source: 'html',
selector: '.bsd-strong-post-training-template'
},
/* ... */
},
/* ... */
edit: class extends Component {
constructor(props) {
super(...arguments);
this.props = props;
/* ... */
this.dayTemplateHandler = this.dayTemplateHandler.bind(this);
this.onChangeBlockTemplate = this.onChangeBlockTemplate.bind(this);
}
/* ... */
dayTemplateHandler(new_val) {
const dayTemplateList = this.state.dayTemplateList;
let selectedDayTemplate = dayTemplateList.filter(item => {
return item.value == new_val;
})
if (selectedDayTemplate[0]['label']) {
this.props.setAttributes({
dayTemplateId: new_val,
dayTemplateName: selectedDayTemplate[0]['label']
});
}
this.getTemplate(new_val);
}
getTemplate(templateId) {
api.getDayTemplate(templateId)
.then((data) => {
if (!data.status || data.status == 0) {
return false;
};
if (!data.day_template) {
return false;
};
this.props.setAttributes({
dayTemplateContent: data.day_template.template_content
});
return data.day_template;
}).catch((err) => {
console.log('getTemplate caught error')
return false;
});
}
onChangeBlockTemplate(value) {
this.props.setAttributes({
dayTemplateContent: value
});
}
/* ... */
render() {
const { dayTemplateHandler, onChangeBlockTemplate, phaseControlHandler, programControlHandler, updateBlockAlignment, updateTextAlignment } = this;
const { block_alignment, dayTemplateId, dayTemplateName, dayTemplateContent, phaseId, phaseName, programAuthor, programId, programName, programPhases, text_alignment } = this.props.attributes;
/* ... */
return [
<InspectorControls>
<PanelBody title={ __('Basics', 'bsd-strong-post') }>
<SelectControl
label={ __('Day', 'bsd-strong-post') }
help={ __('The training session (e.g., Day One)', 'bsd-strong-post') }
value={ dayTemplateId }
options={ this.state.phaseTemplates }
onChange={ dayTemplateHandler }
/>
}
</PanelBody>
</InspectorControls>,
<div className='bsd-strong-post-block-editor'>
<div className={ this.props.className }>
<RichText
placeholder={ __('Log your lifts here') }
value={ dayTemplateContent }
multiline={ false }
onChange={ onChangeBlockTemplate }
className='bsd-strong-post-training-log'
/>
</div>
</div>
];
}
},
save: (props) => {
return (
<div className={ `align${props.attributes.block_alignment}` }>
<ul className='list-unstyled'style={{ textAlign: props.attributes.text_alignment }}>
<li>
<strong>{ __('Program', 'bsd-strong-post') }: </strong>
<span className='bsd-strong-post-program'>{ props.attributes.programName }</span>
</li>
<li>
<strong>{ __('Phase', 'bsd-strong-post') }: </strong>
<span className='bsd-strong-post-phase-ph'>{ props.attributes.phaseName }</span>
</li>
<li>
<strong>{ __('Day', 'bsd-strong-post') }: </strong>
<span className='bsd-strong-post-day-ph'>{ props.attributes.dayTemplateName }</span>
</li>
<li>
<strong>{ __('Author', 'bsd-strong-post') }: </strong>
<span className='bsd-strong-post-author-ph'>{ props.attributes.programAuthor }</span>
</li>
</ul>
<RichText.Content
value={ props.attributes.dayTemplateContent }
className='bsd-strong-post-training-log'
/>
</div>
)
}
});
Here's the console output on reload:
Content generated by 'save' function:
<div class="wp-block-bsd-strong-post-training-session alignwide"><ul class="list-unstyled"><li><strong>Program: </strong><span class="bsd-strong-post-program">Madcow</span></li><li><strong>Phase: </strong><span class="bsd-strong-post-phase-ph">Intermediate</span></li><li><strong>Day: </strong><span class="bsd-strong-post-day-ph">Day 1</span></li><li><strong>Author: </strong><span class="bsd-strong-post-author-ph">Madcow</span></li></ul>
<thead>
<tr>
<th scope="col">Exercise</th>
<th scope="col">Set 1</th>
<th scope="col">Set 2</th>
</tr>
</thead>
<tbody>
<tr class="bsd-strong-post-exercise-one">
<td class="bsd-strong-post-exercise-name">Squat</td>
<td class="bsd-strong-post-set-1">95 x 5</td>
<td class="bsd-strong-post-set-2">135 x 5</td>
</tr>
</tbody>
</div>
Content retrieved from post body:
<div class="wp-block-bsd-strong-post-training-session alignwide"><ul class="list-unstyled"><li><strong>Program: </strong><span class="bsd-strong-post-program">Madcow</span></li><li><strong>Phase: </strong><span class="bsd-strong-post-phase-ph">Intermediate</span></li><li><strong>Day: </strong><span class="bsd-strong-post-day-ph">Day 1</span></li><li><strong>Author: </strong><span class="bsd-strong-post-author-ph">Madcow</span></li></ul><table class='bsd-strong-post-training-template'>
<thead>
<tr>
<th scope='col'>Exercise</th>
<th scope='col'>Set 1</th>
<th scope='col'>Set 2</th>
</tr>
</thead>
<tbody>
<tr class='bsd-strong-post-exercise-one'>
<td class='bsd-strong-post-exercise-name'>Squat</td>
<td class='bsd-strong-post-set-1'>95 x 5</td>
<td class='bsd-strong-post-set-2'>135 x 5</td>
</tr>
</tbody>
</table></div>
I can see that the content displayed below Content generated by 'save' function: is missing the <table> and </table> tags. I've tried to work around this by adding tagName='table' in the RichText.Content properties inside the save function, but then the console shows duplicate <table> and </table> tags.
EDIT: The table is populated when a user makes a change to the Select control in InspectorControls. This action calls dayTemplateHandler, which among other things, calls getTemplate, a function that gets the content of the table from the database. Here's an example of that output (data.day_template.template_content):
<table class='bsd-strong-post-training-template'>
<thead>
<tr>
<th scope='col'>Exercise</th>
<th scope='col'>Set 1</th>
<th scope='col'>Set 2</th>
</tr>
</thead>
<tbody>
<tr class='bsd-strong-post-exercise-one'>
<td class='bsd-strong-post-exercise-name'>Squat</td>
<td class='bsd-strong-post-set-1'>95 x 5</td>
<td class='bsd-strong-post-set-2'>135 x 5</td>
</tr>
</tbody>
</table>

On reviewing the table template and considering the error, I suspect the issue is the selector of the dayTemplateContent attribute, .bsd-strong-post-training-template
The first time the content is saved, it successfully loads the template data from database and saves the complete table structure. When the content is reloaded, the block validator fails as the selector of dayTemplateContent reads in the child nodes of the table's css selector (which is thead) and doesn't match expected content. Ref: HTML example of blockquote/paragraphs
Try wrapping the <table> template with a <div class="bsd-strong-post-training-template"> or changing the selector.

Related

How to display an ACF checkbox value in MPDF?

I have been working with mpdf and acf to generate a pdf. I can generate the pdf and display text values but I can't get it to display the values of a checkbox, it displays nothing.
This is the code that I have, what am I doing wrong? How do I get it to display something for the checkbox?
$offer is the checkbox that I am trying to display.
add_action('init', 'congres_redirect');
function congres_redirect() {
if(isset($_GET['offer'])) {
global $post; //ADD THIS
$offerid = $_GET['offer'];
$restname = get_field('restaurant_name', $offerid);
$offer = get_field_object('restaurant_offer', $offerid);
if( in_array( '2courses10', $offer ) or '2courses10' == $offer ) { $offer2for10='2 courses for 10'; }
$randNum = strtoupper(generateRandomString(5));
$date = date("Ymd");
$namecode = strtoupper(str_replace(' ', '', $restname));
$namestr = substr($namecode, 0, 6);
view_conferinta($restname, $randNum, $date, $namecode, $namestr);
}
}
function view_conferinta($restname, $randNum, $date, $namecode, $namestr) {
global $post;
$output = '<html>
<head><title>'.$restname.' | Eat Leeds</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head>
<body style="font-family:chelvetica;">
<table class="voucher-content" width="100%">
<tr>
<td width="60%"></td>
<td width="40%">
<table>
<tr class="inner-voucher">
<td class="offer-details" style="color: #fff !important;">'.$restname.'</td>
</tr>
<tr class="inner-voucher">
<td class="offer-details" style="color: #fff !important;">'.$offer2for10.'</td>
</tr>
<tr>
<td style="vertical-align: top; padding-top: 20px; padding-left: 280px; color: #fff;"><div class="vouchercode">Voucher Code: EL-'.$namestr.''.$date.'-'.$randNum.'</div></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>';
require_once __DIR__ . '/mpdf/vendor/autoload.php';
$mpdf = new \Mpdf\Mpdf(['debug' => true]);
$mpdf->WriteHTML($output);
$mpdf->Output('eatleeds-EL-'.$namestr.''.$date.'-'.$randNum.'.pdf','I');
exit;
}
You did not set a default value for $offer2for10.
You could also use a switch and loop into $offer value:
switch ($offer) {
case '2courses10':
$offer2for10 = "2 courses for 10";
break;
default:
$offer2for10 = "Unknown offer";
break;
}

Angular9 Multiple Checkbox Delete

I Use Table with selection On Angular Material For Multiple Delete I'm Newbie On angular
Now My Project Use .net C# Angular And Basic CRUD
I Want Return Object or Array For Use on My Controller But On my Code It Return "0"
This My Code
Html
<button (click)="DeleteData()" value="Delete">DELETE</button>
<div class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="select">
<th style="width: 100px;" mat-header-cell *matHeaderCellDef>
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"></mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)" [aria-label]="checkboxLabel(row)"></mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="Name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element">{{element.Name}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"(click)="selection.toggle(row)"></tr>
</table>
</div>
Component
refreshList() {
this.service.getdetail()
.subscribe(data => {
this.dataSource = new MatTableDataSource(data);
})
}
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = !!this.dataSource && this.dataSource.data.length;
return numSelected === numRows;
}
masterToggle() {
this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach(r => this.selection.select(r));
}
checkboxLabel(row: detail): string {
if (!row) {
return `${this.isAllSelected() ? 'select' : 'deselect'} all`;
}
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.Id + 1}`;
}
DeleteData() {
const numSelected = this.selection.selected;
this.service.deleteData(numSelected).subscribe(result => {
alert(result);
this.refreshList();
})
}
Services
deleteData(num: detail[]): Observable< string > {
return this.http.post< string>(this.rootURL + '/WebAPI/DeleteRecord', num);
}
export class detail {
Id: number;
Name: string;
}
This My Result Picture

Styling material ui table cells according to their values

I have a material ui table and I would like to colour the different cells according to what value is displayed in them. The cells are populated with json data using map. For example if a cell has the value 1, I would like the colour to be yellow.
{
Name: "A Person",
Attendence: [
{
date: "2019/12/01",
attendence: 1
},
{
date: "2019/12/02",
attendence: 1
},
{
date: "2019/12/03",
attendence: 0
}
]
}
];
return (
<Fragment>
{attendence.map(person => {
return (
<Table>
<thead>
<tr>
<th>Name</th>
{person.Attendence.map(personAttendendance => {
return <th>{personAttendendance.date}</th>;
})}
</tr>
</thead>
<tbody>
<tr>
<td>{person.Name}</td>
{person.Attendence.map(personAttendendance => {
return <td>{personAttendendance.attendence}</td>;
})}
</tr>
</tbody>
</Table>
);
})}
</Fragment>
);
}
export default Test;
That is what the table looks like. I tried
if(value === 1){
return(
<TableCell style={{ background: "red" }}>{value}</TableCell>
)
} else {
return(
<TableCell style={{ background: "red" }}>{value}</TableCell>
)
}
}
But that did not work . It just read the else and made everything red.
Change your tbody in test.js to:
<tbody>
<tr>
<td>{person.Name}</td>
{person.Attendence.map(personAttendendance => {
if(personAttendendance.attendence === 1){
return <td style={{background: "red" }}>{personAttendendance.attendence}</td>;
} else {
return <td style={{background: "blue" }}>{personAttendendance.attendence}</td>;
}
})}
</tr>
</tbody>
or
<tbody>
<tr>
<td>{person.Name}</td>
{person.Attendence.map(personAttendendance => {
return <td style={{background: personAttendendance.attendence === 1 ? "red" : "blue"}}>{personAttendendance.attendence}</td>;
})}
</tr>
</tbody>
Which ever suits you best.
Link to fork here. (using the second example)

HowTo: Pass props to page defined styled component?

So I'm having an issue dynamically passing the background colour of a button to an styled component I have defined. If I import the styled component as follows:
import styled from 'styled-components';
const CartQuantityButton = styled.button`
background: ${props => props.background};
...
`;
export default CartQuantityButton;
Mycomponent .js
import CartQuantityButton from './styles/CartQuantityButton';
return (
<CartItemStyles>
<Mutation
mutation={UPDATE_CART_ITEM_MUTATION}
variables={this.state}
>
{(updateCartItem, { loading, error }) => {
return (
<Form2>
<table width="100%" border="0" cellPadding="0">
<tr>
<td><CartQuantityButton background={loading ? 'grey' : 'red'} type="submit">Updat{loading ? 'ing' : 'e'}</CartQuantityButton></td>
</tr>
</table>
</Form2>
)
}}
</Mutation>
</CartItemStyles>
)
everything works as expected, the background colour switches from red to grey. But if I define the styled component as part of MyComponent.js, as follows:
const Form2 = styled.form`
...
button,
input[type='submit'] {
background: ${props => props.background};
}
`;
return (
<CartItemStyles>
<Mutation
mutation={UPDATE_CART_ITEM_MUTATION}
variables={this.state}
>
{(updateCartItem, { loading, error }) => {
return (
<Form2>
<table width="100%" border="0" cellPadding="0">
<tr>
<td><button background={loading ? 'grey' : 'red'} type="submit">Updat{loading ? 'ing' : 'e'}</button></td>
</tr>
</table>
</Form2>
)
}}
</Mutation>
</CartItemStyles>
)
the background colour of the <button/> isn't picked up. What am I overlooking here?

Jquery ui theme overwriting datatables css

I can't figure out why my jquery ui themeroller won't play nice with my datatables I'm trying to include in a dialog. The datatable uses the jquery-ui color and the columns are not readable. Am I missing something?
Here is my HTMl:
<div id="dvAdvanced" class="display" style="padding-top:40px;">
<table id="tblAdvSearch">
<thead>
<tr>
<th>Drawing #</th>
<th>Project</th>
<th>Customer</th>
<th>Created By</th>
<th>Date Created</th>
</tr>
</thead>
<tbody>
<tr>
<td>1#Html.DisplayFor(model => model.PartNumber)</td>
<td>2#Html.DisplayFor(model => model.RFQ)</td>
<td>3#Html.DisplayFor(model => model.customer_id)</td>
<td>#Html.DisplayFor(model => model.CreatedBy)</td>
<td>#Html.DisplayFor(model => model.DateCreated)</td>
</tr>
</tbody>
</table>
</div>
Here is my javascript:
$(document).ready(function () {
$("#btnAdvSearch").button();
$("#btnGenPart").button();
dlgAdvSearch = $("#dlgAdvSearch").dialog({
"jQueryUI": true,
title: "Advanced Search",
autoOpen: false,
height: 500,
width: 900,
modal: true,
buttons: {
Close: function () {
dlgAdvSearch.dialog("close");
}
}
});
$("#tabs").tabs();
$("#tblQuickSearch").dataTable();
$("#tblAdvSearch").dataTable();
$("#btnAdvSearch").button().on("click", function () {
dlgAdvSearch.dialog("open");
});;
});
And finally here is my bundleconfig:
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css",
"~/Content/jquery-ui.min.css",
"~/Content/jquery-ui.structure.min.css",
"~/Content/jquery-ui.theme.min.css",
"~/Content//DataTables/css/jquery.dataTables.css",
"~/Content//DataTables/css/dataTables.jqueryui.css",
"~/Content/DataTables/css/jquery.dataTables_themeroller.css",
"~/Content/DataTables/css/dataTables.bootstrap.css"));

Resources