본문 바로가기
IT/mendix

Pluggable 웹 위젯 만들기 - 2 ( 공식문서 따라하기 )

by 가능성1g 2024. 8. 27.
반응형

이전글 : 

Pluggable 웹 위젯 만들기 - 1 ( 공식문서 따라하기 ) (tistory.com)

 

Pluggable 웹 위젯 만들기 - 1 ( 공식문서 따라하기 )

0. node.js lts 버전을 설치한다. ( 저는 20.16.0 버전을 설치하고 실행했습니다. ) 1. 멘딕스 실행후, Blank 웹으로 프로젝트 생성2. 샘플 엔티티 생성3. 홈에 Data View 위젯을 추가하고,데이터 소스로 마이

taisou.tistory.com

 

1. 수정권한 설정하기 ( 수정 가능 변경 )

 

1.1 수정여부 시스템 프로퍼티 추가

수정 src/TextBox.xml

<?xml version="1.0" encoding="utf-8"?>
<widget id="kjbank.textbox.TextBox" pluginWidget="true" needsEntityContext="true" offlineCapable="true"
        supportedPlatform="Web"
        xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd">
    <name>Text Box</name>
    <description>Edit text input</description>
    <icon/>
    <properties>
        <propertyGroup caption="Label">
            <systemProperty key="Label" />
        </propertyGroup>
        <propertyGroup caption="Data source">
            <property key="textAttribute" type="attribute">
                <caption>Attribute (path)</caption>
                <description/>
                <attributeTypes>
                    <attributeType name="String"/>
                </attributeTypes>
            </property>
        </propertyGroup>
        <propertyGroup caption="Editability">
            <systemProperty key="Editability" />
        </propertyGroup>
    </properties>
</widget>

 

수정 src/TextBox.tsx

import { ReactElement, createElement } from "react";
import { TextBoxContainerProps } from "../typings/TextBoxProps";
import { TextInput } from "./components/TextInput";

import "./ui/TextBox.css";

export function TextBox(props: TextBoxContainerProps): ReactElement {
    const value = props.textAttribute.value || "";
    return <TextInput
        value={value}
        onChange={props.textAttribute.setValue}
        tabIndex={props.tabIndex}
        disabled={props.textAttribute.readOnly}
    />;
}

 

수정 src/components/TextInput.tsx

import { createElement, ReactElement, CSSProperties } from "react";

export interface TextInputProps {
    value: string;
    className?: string;
    style?: CSSProperties;
    tabIndex?: number;
    onChange?: (value: string) => void;
    disabled?: boolean;
}

export function TextInput({value, onChange, tabIndex, style, className, disabled }: TextInputProps): ReactElement {
    return <input
        type="text" value={value}
        onChange={event => {
            onChange?.(event.target.value);
        }}
        className={"form-control " + className}
        style={style}
        tabIndex={tabIndex}
        disabled={disabled}
    />;
}

 

1.2 Mendix Studio Pro 에서 싱크(F4) 후 , 변경사항 적용 확인

 

2. 시스템 프로퍼티를 그룹으로 묶어서 표기 하기

수정 src/TextBox.xml

<?xml version="1.0" encoding="utf-8"?>
<widget id="kjbank.textbox.TextBox" pluginWidget="true" needsEntityContext="true" offlineCapable="true"
        supportedPlatform="Web"
        xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd">
    <name>Text Box</name>
    <description>Edit text input</description>
    <icon/>
    <properties>
        <propertyGroup caption="General">
            <propertyGroup caption="Data source">
                <property key="textAttribute" type="attribute" onChange="onChangeAction" >
                    <caption>Attribute (path)</caption>
                    <description/>
                    <attributeTypes>
                        <attributeType name="String"/>
                    </attributeTypes>
                </property>
            </propertyGroup>
            <propertyGroup caption="Label">
                <systemProperty key="Label" />
            </propertyGroup>
            <propertyGroup caption="Editability">
                <systemProperty key="Editability"/>
            </propertyGroup>
            <propertyGroup caption="Visibility">
                <systemProperty key="Visibility"/>
            </propertyGroup>
            <propertyGroup caption="Validation">
                <property key="requiredMessage" type="textTemplate" required="false">
                    <caption>Required message</caption>
                    <description/>
                    <translations>
                        <translation lang="en_US">A input text is required</translation>
                    </translations>
                </property>
            </propertyGroup>
        </propertyGroup>
        <propertyGroup caption="Common">
            <systemProperty key="Name"/>
            <systemProperty key="TabIndex"/>
        </propertyGroup>
        <propertyGroup caption="Events">
            <propertyGroup caption="Events">
                <property key="onChangeAction" type="action" required="false">
                    <caption>On change</caption>
                    <description/>
                </property>
            </propertyGroup>
        </propertyGroup>
    </properties>
</widget>

 

3. 디자인모드의 프리뷰를 보여지게 하기 위한 수정

src/TextBox.editorPreview.tsx

import { createElement, ReactElement } from "react";
import { TextBoxPreviewProps } from "../typings/TextBoxProps";
import { TextInput } from "./components/TextInput";

export function preview(props: TextBoxPreviewProps): ReactElement {
   return <TextInput value={`[${props.textAttribute}]`} />;
}

export function getPreviewCss(): string {
   return require("./ui/TextBox.css");
}

 

4. 커스텀 위젯 TextInput 에 OnBlur 이벤트 추가하고, 데이터 수정 추가

src/components/TextInput.tsx

import { createElement, ReactElement, CSSProperties, useState, useEffect, } from "react";

export interface TextInputProps {
    id?: string;
    value: string;
    className?: string;
    style?: CSSProperties;
    tabIndex?: number;
    hasError?: boolean;
    required?: boolean;
    disabled?: boolean;
    onLeave?: (value: string, changed: boolean) => void;
}

interface TextInputState {
    editedValue?: string;
}

export function TextInput({value, onLeave, tabIndex, style, className, disabled, id, hasError, required }: TextInputProps): ReactElement {
    const [state, setState] = useState<TextInputState>({ editedValue: undefined });
    useEffect(() => setState({ editedValue: undefined }), [value]);

    function getCurrentValue(): string {
        return state.editedValue !== undefined ? state.editedValue : value;
    }

    function onBlur(): void {
        onLeave?.(getCurrentValue(), getCurrentValue() !== value);
        setState({ editedValue: undefined });
    }

    return <input
        id={id}
        type="text"
        value={getCurrentValue()}
        onChange={event => setState({ editedValue: event.target.value })}
        onBlur={onBlur}
        className={"form-control " + className}
        disabled={disabled}
        style={style}
        tabIndex={tabIndex}
        aria-labelledby={`${id}-label`}
        aria-invalid={hasError}
        aria-required={required}
    />;
}

 

5. 오류 알림 id 추가

src/components/Alert.tsx

import { ReactNode, createElement, ReactElement } from "react";

export interface AlertProps {
    id?: string;
    alertStyle?: "default" | "primary" | "success" | "info" | "warning" | "danger";
    className?: string;
    children?: ReactNode;
}

export const Alert = ({ alertStyle = "danger", className, children, id } : AlertProps ) : ReactElement  | null =>
    children
        ? <div id={id} className={`alert alert-${alertStyle} mx-validation-message ${className}`}>
            {children}
        </div>
        : null;

Alert.displayName = "Alert";

 

6. TextInput 을 가지고 있는 TextBox 수정

src/TextBox.tsx

import { ReactElement, createElement, Fragment, useEffect } from "react";
import { TextBoxContainerProps } from "../typings/TextBoxProps";
import { TextInput } from "./components/TextInput";

import "./ui/TextBox.css";
import { Alert } from "./components/Alert";

export function TextBox(props: TextBoxContainerProps): ReactElement {
    const value = props.textAttribute.value || "";
    const validationFeedback = props.textAttribute.validation;
    /* !! -> 명시적으로 boolean 타입으로 변환 */
    const required = !!(props.requiredMessage && props.requiredMessage.value);

    useEffect(() => {
        props.textAttribute.setValidator((value?: string): string | undefined => {
            if (!value) {
                return props.requiredMessage?.value ?? "";
            }
        });
    }, []);

    function onLeave(value: string, isChanged: boolean): void {
        if (!isChanged) {
            return;
        }
        props.textAttribute.setValue(value);
    }

    return (
        <Fragment>
            <TextInput
                id={props.id}
                value={value}
                onLeave={onLeave}
                tabIndex={props.tabIndex}
                disabled={props.textAttribute.readOnly}
                required={required}
                hasError={!!validationFeedback}
            />
            <Alert id={props.id}>{validationFeedback}</Alert>
        </Fragment>
    );
}

 

7. 테스트 

Text Box 에 글을 쓰고 Leave 이벤트를 발생 시키면 Sample Data 에 데이터 입력

Text Box 에 아무것도 않쓰고  Leave 이벤트를 발생시키면 오류 알림

 

반응형