PhotoEditorSDK Uncaught TypeError: Cannot read property 'export' of null - photoeditorsdk

Currently using photoeditorsdk#4.3.1
With pure JS example, export image works just fine.
window.onload = () => {
const container = document.getElementById('app')
const editor = new PhotoEditorSDK.UI.ReactUI({
container,
license: JSON.stringify(PESDK_LICENSE),
assets: {
baseUrl:'/assets'
},
})
window.editor = editor
}
When try to wrap photoeditorsdk into a React Component as following
class Editor extends Component {
constructor (props) {
super(props)
}
componentDidMount () {
const container = this.refs.pesdk
const editor = new PhotoEditorSDK.UI.ReactUI({
container,
license: JSON.stringify(PESDK_LICENSE),
assets: {
baseUrl: '/assets'
},
title: 'Photo Editor'
})
window.editor = editor
}
render () {
return (
<div ref='pesdk'></div>
)
}
}
export default Editor
and export image using
editor.export(false).then(image => document.body.append(image))
from window.editor will encounter the errror
react-dom.development.js:5622 Uncaught TypeError: Cannot read property 'export' of null
at t.export (react-dom.development.js:5622)
at <anonymous>:1:14

I'm not able to reproduce this here. Can you give more details? E.g., where and when are you calling editor.export from. Also, are you passing an image to the editor as an option?

Related

Uncaught Livewire: Directive already registered: [sortable]

I am getting this error because I'm trying to install https://github.com/livewire/sortable to my existing project that is using https://filamentphp.com/ (but just the Forms only)
In my app.js
import 'livewire-sortable'
Then I ran it to my browser.
But in my console, it says Uncaught Livewire: Directive already registered: [sortable].
That's when I investigated it, Then I found sortable.js https://github.com/filamentphp/filament/blob/2.x/packages/forms/resources/js/sortable.js on the filament/forms
import Sortable from 'sortablejs'
window.Sortable = Sortable
window.Livewire.directive('sortable', (el) => {
el.sortable = Sortable.create(el, {
draggable: '[wire\\:sortable\\.item]',
handle: '[wire\\:sortable\\.handle]',
dataIdAttr: 'wire:sortable.item',
})
})
export default (Alpine) => {
Alpine.directive('sortable', (el) => {
el.sortable = Sortable.create(el, {
draggable: '[x-sortable-item]',
handle: '[x-sortable-handle]',
dataIdAttr: 'x-sortable-item',
})
})
}
Now, is there a way I can change the directive into livewireSortable so that It will not affect the Filament Forms?

How to pass an object from axios catch block to parent component with Vue.js

I am using Laravel 7 and Vue.js 2.
I want to pass an object of validation errors from the catch block of an axios call to a parent component but for some reasons it doesn't work.
This is the code of the axios call:
runReport: function() {
let self = this;
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
this.errors = {};
})
.catch(function(error) {
console.log(error.response.data);
self.errors = error.response.data;
alert(self.errors);
self.$emit('failure');
self.$emit('passErrors', self.errors); //problem
console.log('call ended');
});
}
This is the code in the parent component:
<template>
<div>
<report-meeting #passMeetings="onPassMeetings" #failure="displayTable=false" #success="displayTable=true"></report-meeting>
<hr>
<validated-errors :errorsMeeting="errorsMeeting" #passErrors="onPassErrors" v-if="displayTable===false"></validated-errors>
<table-report :meetingsSelected="meetingsSelected" v-if="displayTable===true"></table-report>
</div>
</template>
<script>
import TableReport from "./TableReport.vue"
import ReportMeeting from "./ReportMeeting.vue"
import ValidatedErrors from "./ValidatedErrors.vue"
export default {
components: {
'table-report': TableReport,
'report-meeting': ReportMeeting,
'validated-errors': ValidatedErrors
},
mounted() {
console.log('Component mounted.');
},
data: function() {
return {
displayTable: false,
meetingsSelected: {},
errorsMeeting: {}
}
},
methods: {
onPassMeetings(value) {
console.log(value);
this.meetingsSelected = value;
},
onPassErrors(value) {
console.log('errors passed'); //never used
this.errorsMeeting = value;
}
}
}
</script>
In the console I visualize no errors (except an 422 Unprocessable Entity). The strange thing is that the first emit works (failure), but the second one doesn't work (passErrors).
In the parent function onPassErrors I put a console.log that is never used so I suppose that the function is never called.
Can help?
This is likely caused by an event name mismatch, which can occur when using in-DOM templates because HTML attributes are automatically lower-cased (#passErrors becomes #passerrors in the DOM).
When using the development build of Vue, you'd see a warning in the browser's console:
[Vue tip]: Event "passerrors" is emitted in component but the handler is registered for "passErrors". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "pass-errors" instead of "passErrors".
This is not a problem in single file components (demo 1) or string templates (demo 2), but if you must stick with in-DOM templates, custom event names are recommended to be kebab-case:
<!-- Parent.vue -->
<MyComponent #pass-errors="onPassEvent" />
// MyComponent.vue
runReport() {
this.$emit('pass-errors', /*...*/)
}
demo 3

this.props.editor.create is not a function in CKEditor - NextJS

I'm following the steps from this. And my CKEditor now can run on my nextjs app. But the problem is when I wanna put simpleUploadAdapter, there is an error message saying props.editor.create is not a function. Here's the code :
import Head from 'next/head'
import styles from '../styles/Home.module.css'
import React, { useState, useEffect, useRef } from 'react'
export default function Home() {
const editorCKRef = useRef()
const [editorLoaded, setEditorLoaded] = useState(false)
const { CKEditor, SimpleUploadAdapter, ClassicEditor } = editorCKRef.current || {}
useEffect(() => {
editorCKRef.current = {
CKEditor: require('#ckeditor/ckeditor5-react'),
// SimpleUploadAdapter: require('#ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter'),
ClassicEditor: require('#ckeditor/ckeditor5-build-classic')
}
setEditorLoaded(true)
}, [])
return (
<div className={styles.container}>
<Head>
<title>My CKEditor 5</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<h2>Using CKEditor 5 build in Next JS</h2>
{editorLoaded && ClassicEditor &&
<CKEditor
name="editor"
editor={ typeof ClassicEditor !== 'undefined' ?
ClassicEditor.create(
document.getElementsByName("editor"), {
plugins: [ SimpleUploadAdapter],
//toolbar: [ ... ],
simpleUpload: {
// The URL that the images are uploaded to.
uploadUrl: 'http://example.com',
// Enable the XMLHttpRequest.withCredentials property.
withCredentials: false
}
}
): ''
}
data="<p>Hello from CKEditor 5!</p>"
onInit={ editor => {
// You can store the "editor" and use when it is needed.
console.log( 'Editor is ready to use!', editor );
} }
onChange={ ( event, editor ) => {
const data = editor.getData();
console.log('ON CHANGE')
// console.log(ClassicEditor.create())
// console.log( { event, editor, data } );
} }
onBlur={ ( event, editor ) => {
console.log( 'Blur.', editor );
} }
onFocus={ ( event, editor ) => {
console.log( 'Focus.', editor );
} }
config={
{
simpleUpload: {
uploadUrl: 'localhost:8000/api/files/upload/question/1'
}
}
}
/>
}
</div>
)
}
and this is the error:
So what's the problem in here? Thank you
I got mine to work by wrapping the CKEditor component in a class component of my own.
class RichTextEditor extends React.Component<Props, State> {
render() {
const { content } = this.props;
return (
<CKEditor
editor={ClassicEditor}
data={content}
/>
);
}
}
It seems CKEditor just doesn't play nice with function components. Then use dynamic import to load the wrapper if you're using NextJS.
const RichTextEditor = dynamic(() => import("/path/to/RichTextEditor"), {
ssr: false,
});
I remembered that CKEditor4 is easier to setup in Next.js.
CKEditor5 require more work, you have to use dynamic import with mode ssr=false
But in your case, you also want to use another plugin SimpleUploadAdapter
I tried using CKEditor React component + build classic + SimpleUploadAdapter but meets the error "Code duplication between build classic and source (SimpleUploadAdapter)".
So I decided to custom the ckeditor5-build-classic, add the plugin into there and rebuild, then make it works :)(https://ckeditor.com/docs/ckeditor5/latest/builds/guides/development/custom-builds.html)
Here are few remarks:
Custom ckeditor5-build-classic
// ckeditor5-build-classic-custom local package
// Add SimpleUploadAdapter into the plugin list
// src/ckeditor.js
import SimpleUploadAdapter from '#ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter';
ClassicEditor.builtinPlugins = [
...
SimpleUploadAdapter
...
]
// Rebuild for using in our app
npm run build
Use the custom build in our app
// app/components/Editor.js
import CKEditor from "#ckeditor/ckeditor5-react";
import ClassicEditor from "#ckeditor/ckeditor5-build-classic";
...
<CKEditor
editor={ClassicEditor}
config={{
// Pass the config for SimpleUploadAdapter
// https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/simple-upload-adapter.html
simpleUpload: {
// The URL that the images are uploaded to.
uploadUrl: "http://example.com",
// Enable the XMLHttpRequest.withCredentials property.
withCredentials: true,
// Headers sent along with the XMLHttpRequest to the upload server.
headers: {
"X-CSRF-TOKEN": "CSRF-Token",
Authorization: "Bearer <JSON Web Token>",
},
},
}}
...
Dynamic import for loading the editor from client-side
// pages/index.js
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("../components/editor"), {ssr: false})
To sum up:
Custom the CKEditor build, add needed plugins... then rebuild. Make them as a local package
Use that local package in our app!
Check my git sample with long comments: https://github.com/nghiaht/nextjs-ckeditor5

Export componenent after ajax call finishes in React

I want to export a component after the ajax call finishes. Here is the below code, the output of below code is
exporting 1
exporting 3
exporting 4
exporting 2
But I want to execute it sequentially, My desired output is
exporting 1
exporting 2
exporting 3
exporting 4
import appLocaleData from "react-intl/locale-data/en";
import enMessages from "../locales/en_US.json";
import config from "config";
const lang = new Object();
console.log( " exporting 1" );
fetch(`${config.api.languageUrl}english.json`, {
method: "GET",
headers: {
"Content-Type": "application/octet-stream"
}
})
.then(res => res.json())
.then(json => {
Object.assign(lang, json);
console.log("json->", json);
console.log("lang->", lang);
console.log(lang._VIEW);
console.log( "exporting 2" );
});
console.log( "exporting 3" );
const EnLang = {
messages: {
...lang
},
locale: "en-US",
data: appLocaleData
};
console.log( "exporting 4" );
export default EnLang;
Is there anyway in react, I can perform this ?
Thanks,
No, there is no such thing as an asynchronous export in javascript. If you can be more clear about what you are trying to accomplish I might be able to suggest a possible approach, but as is I don't even understand how this has anything to do with React specifically
EDIT based on OP's reply:
try something like this...
export const LocalsContext = React.createContext([]);
const App = () => {
...
const [locals, setLocals] = useState([]);
useEffect(() => {
fetch(...)
.then(...)
.then(localsList => setLocals(localsList)
}, []);
return (
<LocalsContext.Provider value={locals}>
...
</LocalsContext.Provider>
)
}
export default App
and then in any component anywhere within your app you can access the locals like so:
const MyComponent = () => {
/*
* will re-render whenever the locals updates,
* i.e., will render once with [] as the value, then once the
* data is fetched it will render again with the actual locals value
*/
const locals = useContext(LocalsContext);
return <div>some component</div>
}

Vue 2, Cannot reference Prop Object in template

Problem: Although from the Vue DevTools I am passing the prop correctly and the router-view component has access to the data that it needs and in the correct format, whenever I try to access any of the data properties from within the template I get Uncaught TypeError: Cannot read property 'name' of null. It's really confusing because from the DevTools everything is a valid object and the properties are not null.
App.js
const game = new Vue({
el: '#game',
data: function() {
return {
meta: null,
empire: null,
planets: null
};
},
created: () => {
axios.get('/api/game').then(function (response) {
game.meta = response.data.meta;
game.empire = response.data.empire;
game.planets = response.data.planets;
});
},
router // router is in separate file but nothing special
});
main.blade.php
<router-view :meta="meta" :empire="empire" :planets="planets"></router-view>
script section of my Component.vue file
export default {
data: function() {
return {
}
},
props: {
meta: {
type: Object
},
empire: {
type: Object
},
planets: {
type: Array
}
}
}
Any ideas? Thanks in advance.
Because of your data is async loading so when my Component.vue renders your data in parent component may not be there. So you need to check if your data is loaded. You can try this code:
{{ meta != null && meta.name }}
PS: Your created hook should be:
created() {
axios.get('/api/game').then((response) => {
this.game.meta = response.data.meta;
this.game.empire = response.data.empire;
this.game.planets = response.data.planets;
});
},
router-view is a component from view-router which can help render named views. You can not pass empire and planets to it as those are props of your component.
You have to have following kind of code to pass empire and planets to your component:
<my-component :meta="meta" :empire="empire" :planets="planets"></my-component>
You can see more details around this here.

Resources