Quantcast
Channel: LoginVast.Com
Viewing all articles
Browse latest Browse all 640

Populate data from mongoDB into Lexical Editor to edit for Blog Post

$
0
0

The post Populate data from mongoDB into Lexical Editor to edit for Blog Post appeared first on LoginVast.Com.

I am trying to show my data inside the Lexical Editor in a Next.js app and then submit the edited data. However I continue to get TypeError: Cannot read properties of undefined (reading ‘type’) when trying to JSON.stringifiy the data but nothing seems to work. When I console log the value it does show the actual data.

Editor.js

import { useEffect, useState } from "react";

/* Lexical Design System */
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
import { ListItemNode, ListNode } from "@lexical/list";
import { CodeHighlightNode, CodeNode } from "@lexical/code";
import { AutoLinkNode, LinkNode } from "@lexical/link";
import { TRANSFORMERS } from "@lexical/markdown";

/* Lexical Plugins Local */
import ToolbarPlugin from "@/utils/plugins/ToolbarPlugin";
import AutoLinkPlugin from "@/utils/plugins/AutoLinkPlugin";
import CodeHighlightPlugin from "@/utils/plugins/CodeHighlightPlugin";
import OnChangePlugin from "@/utils/plugins/onChangePlugin";

/* Lexical Plugins Remote */
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin";

/* Lexical Others */
import { LexicalComposer } from "@lexical/react/LexicalComposer";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import ExampleTheme from "@/utils/theme/EditorTheme";

function Placeholder() {
    return <div className="editor-placeholder">Enter some rich text...</div>;
}

const Editor = ({onChange, value}) => {
    const [isMounted, setIsMounted] = useState(false)

    const [floatingAnchorElem, setFloatingAnchorElem] = useState(null)

    const onRef = (_floatingAnchorElem) => {
        if (floatingAnchorElem !== null) {
        setFloatingAnchorElem(floatingAnchorElem)
        }
    }

    const editorConfig = {
        namespace: 'RichTextEditor',
        theme: ExampleTheme,
        onError(error) {
            throw error;
        },
        ...(value && {
            editorState: typeof value !== '' ? JSON.stringify(value) : value,
        }),
        nodes: [
            HeadingNode,
            ListNode,
            ListItemNode,
            QuoteNode,
            CodeNode,
            CodeHighlightNode,
            TableNode,
            TableCellNode,
            TableRowNode,
            AutoLinkNode,
            LinkNode
        ],
    };

    useEffect(() => {
        setIsMounted(true);
    }, [])

    if (!isMounted) return null
  return (
    <LexicalComposer initialConfig={editorConfig}>
    <div className="editor-container">
        <ToolbarPlugin />
        <div className="editor-inner" ref={onRef}>
            <RichTextPlugin
                contentEditable={<ContentEditable className="editor-input" />}
                placeholder={<Placeholder />}
                ErrorBoundary={LexicalErrorBoundary}
            />
            <ListPlugin />
            <HistoryPlugin />
            <AutoFocusPlugin />
            <CodeHighlightPlugin />
            <LinkPlugin />
            <TabIndentationPlugin />
            <AutoLinkPlugin />
            <MarkdownShortcutPlugin transformers={TRANSFORMERS} />
            {floatingAnchorElem && (
              <FloatingLinkEditorPlugin anchorElem={floatingAnchorElem} />
            )}
            <OnChangePlugin onChange={onChange} />
        </div>
    </div>
</LexicalComposer>
  )  
}

export default Editor;

OnChangePlugin.js

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {useEffect} from 'react';
import { $generateHtmlFromNodes } from "@lexical/html";

 export default function OnChangePlugin({ onChange }) {
    const [editor] = useLexicalComposerContext();
    
    useEffect(() => {
      return editor.registerUpdateListener(({editorState}) => {

        editor.update(() => {
            const htmlString = $generateHtmlFromNodes(editor, null);
            onChange({ content: htmlString, editorState })
         });

      });
    }, [editor, onChange]);
  
  }

BlogEdit.js


const BlogEdit = ({ params }) => {
    const blogId = params.id;
    const [{ loading, error, loadingUpdate, loadingUpload }, dispatch] =
    useReducer(reducer, {
      loading: true,
      error: '',
    });

    const {
        register,
        control,
        handleSubmit,
        formState: { errors },
        setValue,
      } = useForm();

    const editorRef = useRef();


    const [isArticle, setIsArticle] = useState('');

    const [isPublished, setIsPublished] = useState(false);
    const [isImageOne, setIsImageOne] = useState('');

    useEffect(() => {
        const fetchData = async () => {
          try {
            dispatch({ type: 'FETCH_REQUEST' });
            const { data } = await axios.get(`/api/blog`);

            dispatch({ type: 'FETCH_SUCCESS' });
            setValue('title', data.title);
            setValue('slug', data.slug);
            setValue('description', data.description);
            setValue('article', data.article);
            setIsArticle('article', data.article)
            setValue('author', data.author);
            setIsPublished(data.published);
            setValue('headerImage', data.headerImage);
            setIsImageOne(data.headerImage);
          } catch (err) {
            dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
          }
        };
    
        fetchData();
      }, [blogId, setValue]);


  const router = useRouter();
  
const submitHandler = async ({
    title,
    tags,
    description,
    author,
    article,
    published = isPublished,
    headerImage,
    slug
  }) => {
    try {
      dispatch({ type: 'UPDATE_REQUEST' });
      await axios.put(`/api/admin/blogs/${blogId}`, {
        title,
        tags,
        description,
        author,
        article,
        published,
        headerImage,
        slug
      });
      dispatch({ type: 'UPDATE_SUCCESS' });
      toast.success('Blog updated successfully', {
        theme: 'colored'
      });
      router.push('/admin/blogs');
    } catch (err) {
      dispatch({ type: 'UPDATE_FAIL', payload: getError(err) });
      toast.error(getError(err), {
        theme: 'colored'
      });
    }
  };

  return (
    <Layout title="Admin Products">
      <ToastContainer 
        position="top-center" 
        draggable={false} 
        transition={Slide} 
        autoClose={5000}
        hideProgressBar={true}
        className="toast-alert"
      />
      <div className="admin-container page-contain bg-white py-5">
        <Container fluid className="pt-5">
          <Row>
            <Col lg={2} className="mb-3">
              <SideNav />
            </Col>
            <Col lg={10}>
              <Card className="card admin-card-container p-4">
                <Card.Body>
                  <Row className="gx-5">
                    {/* <Editor /> */}
                    <Card.Title className="text-center text-primary fs-1">{`Edit Category ${blogId}`}</Card.Title>
                    {loading ? (
                      <div className="spinner-border customer-spinner text-primary" role="status">
                        <span className="visually-hidden">Loading...</span>
                      </div>   
                    ) : error ? (
                      <div className="alert alert-danger" role="alert">
                        {error}
                      </div>          
                    ) : (
                      <form onSubmit={handleSubmit(submitHandler)} className="col-lg-12 col-md-12 col-sm-12 form-shipment justify-content-center">
                        <Row>
                        <Col lg={12}>
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="title"
                              placeholder="Title" 
                              {...register('title', {
                                required: 'Please enter blog title',
                              })}
                            />
                            {errors.title && (
                              <div className="invalid-feedback">
                                {errors.title.message}
                              </div>
                            )}
                            <label htmlFor="title">Title</label>
                          </div>
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="slug"
                              placeholder="Slug"
                              {...register('slug', {
                                required: 'Please enter slug',
                              })}
                            />
                            {errors.slug && (
                              <div className="invalid-feedback">{errors.slug.message}</div>
                            )}
                            <label htmlFor="slug">Slug</label>
                          </div>
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="author"
                              placeholder="Author"
                              {...register('author', {
                                required: 'Please enter an author',
                              })}
                            />
                            {errors.author && (
                              <div className="invalid-feedback">{errors.author.message}</div>
                            )}
                            <label htmlFor="author">Author</label>
                          </div>
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="description"
                              placeholder="Description"
                              {...register('description', {
                                required: 'Please enter an description',
                              })}
                            />
                            {errors.author && (
                              <div className="invalid-feedback">{errors.description.message}</div>
                            )}
                            <label htmlFor="description">Description</label>
                          </div>
                          {isImageOne && ( <Image src={isImageOne} width={150} height={90} alt="Product Image One" /> )}
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="headerImage"
                              placeholder="Blog Header Image" 
                              {...register('headerImage', {
                                required: 'Please upload a blog header image',
                              })}
                            />
                            {errors.headerImage && (
                              <div className="invalid-feedback">{errors.headerImage.message}</div>
                            )}
                            <label htmlFor="headerImage">Blog Header Image</label>
                            <div className="file btn btn-lg btn-primary">
                              Upload Image
                              <input 
                                type="file" 
                                id="imageFile"
                              />
                              {loadingUpload && <Spinner as="span" animation="grow" size="sm" role="status" aria-hidden="true" />}
                            </div>
                          </div>
                          <div className="form-floating">
                          <Controller
                            control={control}
                            name="article"
                            render={({ field: fieldContent }) => (
                              <Controller
                                control={control}
                                name="article"
                                render={({ field }) => (
                                  <Editor
                                    onChange={value => {
                                      field.onChange(value.editorState)
                                      fieldContent.onChange(value.content)
                                    }}
                                    value={JSON.stringify(field.value)}
                                  />
                                )}
                              />
                            )}
                          />
                          </div>
                          <div className="form-floating">
                            <input
                              type="text"
                              className="form-control"
                              id="tags"
                              placeholder="Tags"
                              {...register('tags')}
                            />
                            <label htmlFor="tags">Tags</label>
                          </div>  
                          <div className="form-check my-3">
                            <input 
                              className="form-check-input" 
                              type="checkbox" 
                              onChange={(e) => setIsPublished(e.target.checked)}
                              checked={isPublished}
                              name="published"
                              id="published"
                            />
                            <label className="form-check-label" htmlFor="published">
                              Publish
                            </label>
                          </div>
                        </Col>
                        <button className="w-100 btn btn-lg btn-primary my-4" type="submit">
                          {loadingUpdate ? (
                            <Spinner
                              as="span"
                              animation="grow"
                              size="sm"
                              role="status"
                              aria-hidden="true"
                            />
                          ) : (
                            "Edit Blog Post"
                          )}
                         </button>
                        </Row>
                      </form>
                    )}
                  </Row>
                </Card.Body>
              </Card>
            </Col>
          </Row>
        </Container>
      </div>
    </Layout>
  )
}

What the console shows

I tried many different things to initialize the editorState in Lexical but no matter what I do I cannot get the data to show in the editor. I am able to add text and edit it which I also am able to submit to the database successfully. However, I need for the data to show in the editor and be able to edit and submit the updated version to the database.

The post Populate data from mongoDB into Lexical Editor to edit for Blog Post appeared first on LoginVast.Com.


Viewing all articles
Browse latest Browse all 640

Trending Articles