I have a problem with validations in dynamically added webComponent in shadowRoot. This validations with translate is working properly in normal Virtual DOM but when i want to dynamically add validations result to Shadow DOM, i18next return not translated value.
In Normal DOM this line
this.message = t(FormApplicationResources.ValidationErrorRequired); return valid string text from database in proper language, but in Shadow DOM same code fragment return key value from DB (not translated)
...
import { t } from "i18next";
class PasswordValidator implements IValidator {
private readonly message: string;
private readonly validPasswordCharactersRegex: RegExp = /[a-zA-Z0-9!##$&()'%*{}\[\]~:;?\-|<>=\\-`.+,/\"]/g;
public type: ValidatorType = ValidatorType.Required;
public constructor(message?: string) {
if (message) {
this.message = message;
} else {
this.message = t(FormApplicationResources.ValidationErrorRequired);
}
}
public validate(value: any, callbacks: ICallbacks): IValidResult {
...
}
}
export default PasswordValidator;
I am using attachchShadow with open mode but does not make any difference.
...
connectedCallback(): void {
this.loadAllAttributes();
const props: any = this.props();
...
this.createPoint();
...
this.render();
}
render(): void {
const props: any = this.props();
if (props.apiEndpoint && props.baseName && props.baseUrl && props.instanceName) {
ReactDOM.render(
<App
lang={props.lang ? props.lang : DEFAULT_SYSTEM_CULTURE}
{...props}
/>
, this.mountPoint
);
}
}
createPoint(): void {
if (!this.mountPoint) {
const shadow: ShadowRoot = this.attachShadow({ mode: "open" });
this.mountPoint = document.createElement("div");
this.mountPoint.id = "Example";
this.mountPoint.style.boxSizing = "example";
shadow.appendChild(this.mountPoint);
...
}
}
Related
I create custom blot,
but for some reason the creation of child items does not work when you press tab at the list item. IndentClass does not work and I do not understand how to connect it. Path IndentClass - quill\formats\indent
// eslint-disable-next-line no-unused-vars
import Quill, { ListItem } from 'quill';
let Block = Quill.import('blots/block');
let Container = Quill.import('blots/container');
class NumberListContainer extends Container {}
NumberListContainer.blotName = 'c-number-list-container';
NumberListContainer.tagName = 'OL';
class NumberListItem extends Block {
static create(value) {
const node = super.create();
console.log(typeof value, value, 'value');
node.setAttribute('data-list', value);
return node;
}
static formats(domNode) {
return domNode.getAttribute('data-list') || undefined;
}
static register() {
Quill.register(NumberListContainer);
}
constructor(scroll, domNode) {
console.log( 'value111');
super(scroll, domNode);
const ui = domNode.ownerDocument.createElement('span');
// eslint-disable-next-line no-unused-vars
const listEventHandler = e => {
if (!scroll.isEnabled()) return;
const format = this.statics.formats(domNode, scroll);
if (format === 'checked') {
this.format('list', 'unchecked');
e.preventDefault();
} else if (format === 'unchecked') {
this.format('list', 'checked');
e.preventDefault();
}
};
ui.addEventListener('mousedown', listEventHandler);
ui.addEventListener('touchstart', listEventHandler);
this.attachUI(ui);
}
format(name, value) {
if (name === this.statics.blotName && value) {
this.domNode.setAttribute('data-list', value);
} else {
super.format(name, value);
}
}
}
NumberListItem.blotName = 'c-number-list';
NumberListItem.tagName = 'LI';
NumberListItem.className = 'c-number-list';
NumberListContainer.allowedChildren = [NumberListItem];
NumberListItem.requiredContainer = NumberListContainer;
Quill.register(NumberListContainer);
Quill.register(NumberListItem);
I'm trying to build the barcode scanner using the html5-qrcode npm package in react js, everything working fine except QR shaded region. I added the qrbox in the configuration but not getting the QR shaded region. The qrbox width and height are not taken. I tried multiple ways not getting the desired result. Thanks in advance.
Here is my component:
import { Html5QrcodeScanner, Html5Qrcode } from "html5-qrcode";
import React from 'react';
const qrcodeRegionId = "html5qr-code-full-region";
class Html5QrcodePlugin extends React.Component {
render() {
return <div id={qrcodeRegionId} />;
}
componentWillUnmount() {
this.html5QrcodeScanner.clear().catch(error => {
console.error("Failed to clear html5QrcodeScanner. ", error);
});
}
componentDidMount() {
function createConfig(props) {
var config = {};
if (props.fps) {
config.fps = props.fps;
}
config.qrbox = { width: 250, height: 250 };
if (props.aspectRatio) {
config.aspectRatio = props.aspectRatio;
}
if (props.disableFlip !== undefined) {
config.disableFlip = props.disableFlip;
}
return config;
}
var config = createConfig(this.props);
var verbose = this.props.verbose === true;
if (!(this.props.qrCodeSuccessCallback )) {
throw "qrCodeSuccessCallback is required callback.";
}
this.html5QrcodeScanner = new Html5QrcodeScanner(
qrcodeRegionId, { facingMode: { exact: "environment"} }, config, verbose);
this.html5QrcodeScanner.render(
this.props.qrCodeSuccessCallback,
this.props.qrCodeErrorCallback);
}
};
export default Html5QrcodePlugin;
I'm trying to create Angular Material Chips as shown on the site, but I keep getting this error about the array being null.
Here's the component
import { Component, Input, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { IStatement } from 'src/Interface/ICorporateStatement';
import { StatementService } from '../services/statement.service';
import { MatChipInputEvent } from '#angular/material/chips';
import {COMMA, ENTER} from '#angular/cdk/keycodes';
export interface Tag{
corporate_statement_link_id: number;
name: string;
}
#Component({
selector: 'app-statement-detail',
templateUrl: './statement-detail.component.html',
styleUrls: ['./statement-detail.component.css']
})
export class StatementDetailComponent implements OnInit {
statement: IStatement;
id: number;
tags: Tag[] = [];
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
constructor(private statementService: StatementService,
private router:ActivatedRoute) { }
ngOnInit(): void {
this.tags = [
{ corporate_statement_link_id: 1, name: 'EDI'}
];
console.log("Tags: ", this.tags);
this.router.queryParams.subscribe(param => {
this.id = param.id;
this.getStatement(this.id);
});
}
addTag(event: MatChipInputEvent): void {
console.log(this.tags);
const input = event.input;
const value = event.value;
console.log("Input: ", input);
console.log("Value: ", value);
console.log("Tags: ", this.tags);
this.tags.push({corporate_statement_link_id: this.statement.corporate_statement_link_id, name: value.trim()});
// // Add our fruit
// if ((value || '').trim()) {
// this.fruits.push({name: value.trim()});
// }
// // Reset the input value
// if (input) {
// input.value = '';
// }
}
removeTag(tag: Tag): void {
console.log("removing");
// const index = this.fruits.indexOf(fruit);
// if (index >= 0) {
// this.fruits.splice(index, 1);
// }
}
// get statement
getStatement(id){
this.statementService.getStatement(id).subscribe(data => {
this.statement = <IStatement>data[0];
//get tags
this.statementService.getTags(this.statement.corporate_statement_link_id)
.subscribe(tag => {
this.tags = <Tag[]>tag;
})
}, error => {
console.log(error);
});
}
}
I've refactored the code and moved things here and there, but still can't figure out why the array is still null.
It looks like this code is setting this.tags to null.
.subscribe(tag => {
this.tags = <Tag[]>tag;
})
It may be an issue with your <Tag[]> cast, or maybe the data coming back is null?
if it is expected you could replace any null value here with an empty array like this:
.subscribe(tag => {
this.tags = <Tag[]>tag || [];
})
and see if that helps.
I'm creating a permission service using react typescript and I ran into the following problem. I have the class:
import {IPermission} from "../interfaces/IPermission";
class PermissionService {
private permissions: IPermission[] = [];
constructor(permissions: IPermission[]) {
this.permissions = permissions;
}
public getValue(key: string): IPermission['value'] {
const perm = this.permissions.find(permission => permission.key === key);
if (!perm) {
throw new Error('Could not find the permission');
}
return perm.value;
}
public modifyPermission(key: string, defaultValue: any, value: any): void {
const perms = [...this.permissions];
for (let i = 0; i < perms.length; i++) {
perms[i].defaultValue = defaultValue;
perms[i].value = value
}
this.permissions = perms;
console.log(perms);
}
public parseActivePermissions(permissions: IPermission[]): IPermission[] {
this.permissions.forEach(permission => {
permissions.forEach(activePermission => {
if (permission.key === activePermission.key) {
permission.defaultValue = activePermission.defaultValue;
permission.value = activePermission.value;
}
})
})
return this.permissions;
}
public getAll(): IPermission[] {
return this.permissions;
}
}
export default PermissionService;
and an AdminPermissions data file
import PermissionService from "../services/permission.service";
import {IPermission} from "../interfaces/IPermission";
import Permissions from "./Permissions";
const service: PermissionService = new PermissionService(Permissions);
service.modifyPermission('canAccessAcp', true, true);
const AdminPermissions: IPermission[] = service.getAll();
export default AdminPermissions;
The problem is, the service.modifyPermission() does not update the defaultValue and value of the permission. It's still false when console logging. Why is that?
UPDATE #1
Changed the file a bit. Still doesn't work. Now I'm directly changing the values, but they still log as false.
class AdminPermissions {
public getAll(): IPermission[] {
const service: PermissionService = new PermissionService(Permissions);
service.permissions.forEach(permission => {
if (permission.key === 'canAccessAcp') {
permission.defaultValue = true;
permission.value = true;
}
})
return service.permissions;
}
}
The problem is that with forEach you are not changing the actual value of each items, so you should do something like this:
class AdminPermissions {
public getAll(): IPermission[] {
const service: PermissionService = new PermissionService(Permissions);
return service.permissions.map(permission => {
if (permission.key === 'canAccessAcp') {
return (
{
...permission,
defaultValue: true,
value: true
}
)
}
return permission
});
}
}
I found a solution.
In the permission.service.ts
public modifyPermission(key: string, defaultValue: any, value: any): void {
const perms: IPermission[] = this.permissions.map(permission => {
if (permission.key === key) {
console.log('found permission');
return {
...permission,
defaultValue,
value
}
}
return permission
})
this.permissions = perms;
}
Okay so I've gotten pretty far in creating the React Component for ChartJS, however when testing I get the following error:
FAIL lib\chart\chart.test.tsx
● renders without crashing
TypeError: Cannot read property 'length' of null
at Object.acquireContext (node_modules/chart.js/src/platforms/platform.dom.js:189:19)
at Chart.construct (node_modules/chart.js/src/core/core.controller.js:72:27)
at new Chart (node_modules/chart.js/src/core/core.js:7:8)
at Chart.Object.<anonymous>.Chart.renderChart (lib/chart/chart.tsx:233:26)
at Chart.Object.<anonymous>.Chart.componentDidMount (lib/chart/chart.tsx:42:10)
at node_modules/react-dom/lib/ReactCompositeComponent.js:264:25
at measureLifeCyclePerf (node_modules/react-dom/lib/ReactCompositeComponent.js:75:12)
at node_modules/react-dom/lib/ReactCompositeComponent.js:263:11
at CallbackQueue.notifyAll (node_modules/react-dom/lib/CallbackQueue.js:76:22)
at ReactReconcileTransaction.close (node_modules/react-dom/lib/ReactReconcileTransaction.js:80:26)
at ReactReconcileTransaction.closeAll (node_modules/react-dom/lib/Transaction.js:209:25)
at ReactReconcileTransaction.perform (node_modules/react-dom/lib/Transaction.js:156:16)
at batchedMountComponentIntoNode (node_modules/react-dom/lib/ReactMount.js:126:15)
at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-dom/lib/Transaction.js:143:20)
at Object.batchedUpdates (node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:62:26)
at Object.batchedUpdates (node_modules/react-dom/lib/ReactUpdates.js:97:27)
at Object._renderNewRootComponent (node_modules/react-dom/lib/ReactMount.js:319:18)
at Object._renderSubtreeIntoContainer (node_modules/react-dom/lib/ReactMount.js:401:32)
at Object.render (node_modules/react-dom/lib/ReactMount.js:422:23)
at Object.<anonymous> (lib/chart/chart.test.tsx:7:12)
at Promise (<anonymous>)
at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:169:7)
× renders without crashing (275ms)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.314s, estimated 3s
Ran all test suites related to changed files.
However, I've spent a long time looking over the code and haven't been able to figure out why it refuses to work properly. The error starts at the renderChart() function at creating a new chart instance. My first guess would be the for some reason it's not registering the canvas element despite being called by its id. But when the content of renderChart is moved into the render() function it still gives the same error. Here's the code being tested:
import * as React from 'react'
import * as ClassNames from 'classnames'
import * as ChartJS from 'chart.js'
const IsEqual = require('lodash.isequal')
const Find = require('lodash.find')
const subChart = require('chart.js')
interface IChartProps {
/** The user-defined classes */
readonly className?: string
readonly width?: number
readonly height?: number
readonly reRender?: boolean
readonly type: ChartJS.ChartType
readonly data: ChartJS.ChartData
readonly options: ChartJS.ChartOptions
readonly getDatasetAtEvent?: Function
readonly getElementAtEvent?: Function
readonly getElementsAtEvent?: Function
readonly onElementsClick?: Function
readonly datasetKeyProvider?: Function
}
interface IChartState {
/** Add your states here */
}
export class Chart extends React.Component<IChartProps, IChartState> {
// tslint:disable-next-line
private chartInstance: any
private shadowData: {}
constructor(props: IChartProps) {
super(props)
}
public componentWillMount() {
// this.chartInstance = undefined
}
public componentDidMount() {
this.renderChart()
}
// public componentWillReceiveProps(nextProps: IChartProps) {}
public shouldComponentUpdate(nextProps: IChartProps, nextState: IChartState) {
const props = this.props
if (nextProps.reRender === true) {
return true
}
if (props.height !== nextProps.height || props.width !== nextProps.width) {
return true
}
if (props.type !== nextProps.type) {
return true
}
if (!IsEqual(props.options, nextProps.options)) {
return true
}
const nextData = this.transformDataProp(nextProps)
if (!IsEqual(this.shadowData, nextData)) {
return true
}
return false
}
// public componentWillUpdate(nextProps: IChartProps, nextState: IChartState) {}
public componentDidUpdate(prevProps: IChartProps, prevState: IChartState) {
if (this.props.reRender) {
this.chartInstance.destroy()
this.renderChart()
return
}
this.updateChart()
}
public transformDataProp(props: IChartProps) {
const data = props.data
if (typeof data === 'function') {
const node = document.getElementById('bar-chart') as HTMLCanvasElement
return data(node)
} else {
return data
}
}
public memoizeDataProps(props?: IChartProps) {
if (!this.props.data) {
return
}
const data = this.transformDataProp(this.props)
this.shadowData = {
...data,
datasets:
data.datasets &&
data.datasets.map((set: string[]) => {
return { ...set }
})
}
return data
}
public updateChart() {
const options = this.props.options
const data = this.memoizeDataProps(this.props)
if (!this.chartInstance) {
return
}
if (options) {
this.chartInstance.options = subChart.helpers.configMerge(
this.chartInstance.options,
options
)
}
let currentDatasets =
(this.chartInstance.config.data &&
this.chartInstance.config.data.datasets) ||
[]
const nextDatasets = data.datasets || []
const currentDatasetKeys = currentDatasets.map(
this.props.datasetKeyProvider
)
const nextDatasetKeys = nextDatasets.map(this.props.datasetKeyProvider)
const newDatasets = nextDatasets.filter(
(d: object) =>
currentDatasetKeys.indexOf(this.props.datasetKeyProvider(d)) === -1
)
for (let idx = currentDatasets.length - 1; idx >= 0; idx -= 1) {
const currentDatasetKey = this.props.datasetKeyProvider(
currentDatasets[idx]
)
if (nextDatasetKeys.indexOf(currentDatasetKey) === -1) {
// deleted series
currentDatasets.splice(idx, 1)
} else {
const retainedDataset = Find(
nextDatasets,
(d: object) => this.props.datasetKeyProvider(d) === currentDatasetKey
)
if (retainedDataset) {
// update it in place if it is a retained dataset
currentDatasets[idx].data.splice(retainedDataset.data.length)
retainedDataset.data.forEach((point: number, pid: number) => {
currentDatasets[idx].data[pid] = retainedDataset.data[pid]
})
// const { data, ...otherProps } = retainedDataset
currentDatasets[idx] = {
data: currentDatasets[idx].data,
...currentDatasets[idx],
...retainedDataset.otherProps
}
}
}
}
// finally add any new series
newDatasets.forEach((d: object) => currentDatasets.push(d))
const { datasets, ...rest } = data
this.chartInstance.config.data = {
...this.chartInstance.config.data,
...rest
}
this.chartInstance.update()
}
public componentWillUnmount() {
this.chartInstance.destroy()
}
public onClickEvent = (event: React.MouseEvent<HTMLCanvasElement>) => {
// this.props.getDatasetAtEvent &&
this.props.getDatasetAtEvent(
this.chartInstance.getDatasetAtEvent(event),
event
)
// this.props.getElementAtEvent &&
this.props.getElementAtEvent(
this.chartInstance.getElementAtEvent(event),
event
)
// this.props.getElementsAtEvent &&
this.props.getElementsAtEvent(
this.chartInstance.getElementsAtEvent(event),
event
)
// this.props.onElementsClick &&
this.props.onElementsClick(
this.chartInstance.getElementsAtEvent(event),
event
)
}
public render() {
const className = ClassNames('chart', this.props.className)
// bar.update()
return (
<div className={className}>
<canvas
id="chart-instance"
width={this.props.width ? this.props.width : '400'}
height={this.props.height ? this.props.height : '400'}
onClick={this.onClickEvent}
/>
</div>
)
}
public renderChart() {
const { options, type, data } = this.props
const node = document.getElementById('chart-instance') as HTMLCanvasElement
// const data = this.memoizeDataProps()
this.chartInstance = new ChartJS(node, {
type,
data,
options
})
}
}
can someone help me figure out why this won't work properly?
It might because of this:
currentDatasets[idx].data.splice(retainedDataset.data.length)
You should have a check on retainedDataset.data also:
if (retainedDataset && retainedDataset.data) { ... }