이전글 :
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 이벤트를 발생시키면 오류 알림