コメントコンポーネントは、リモート要求オプションを設定するためのインターフェースを提供します。データソース機能を有効にすると、変更されたデータをリモートデータベースと同期することが可能になります。
データソース機能の設定
dataSourceの定義でenabledプロパティをtrueに設定し、remoteプロパティを使用してリクエスト処理を実装します。remoteプロパティでコメント、ユーザー、リアクションに関する処理を設定することで、データの取得や更新、削除などの操作を実行できるようになります。
設定例は以下の通りです。
コメントの操作に伴う要求と応答
コメントの読み込み、追加、更新、削除を行う場合、コメントに対する操作を伴うリクエストが開始され、サーバーに送信されます。
操作
要求の種類
要求データ
応答データ
読み込み
GET
なし(取得するコメントの種類に応じて、typeなどのクエリパラメータが付与される)
コメントレコードの配列
追加
POST
追加されたコメント(userId, content, parentId?, mentionInfo?)
追加されたデータ
更新
PUT
更新されたコメント(userId, content, parentId?, mentionInfo?)
更新されたデータ
削除
DELETE
削除されたコメントのID
制限なし
※ デフォルトでは、POST/PUTリクエストの要求データ形式はフォームデータになります。
コメントの読み込みのGETリクエストには、操作に応じて以下のクエリパラメータが自動的に付与されます。クライアントサイドで明示的に指定する必要はありません。サーバーサイドでは、リクエストに含まれるパラメータを受け取り、データ処理を行います。
クエリパラメータ
説明
設定例
type
取得コメントの種類。page, sticked, reply, link を指定できる
${url}?type=page&pageNumber=0&pageSize=2${url}?type=sticked${url}?type=reply&commentId=1&loadedCount=0${url}?type=link&url=xxxxx
pageNumber
動的読み込み時のページ番号
${url}?type=page&pageNumber=0&pageSize=2
pageSize
1ページあたりのコメント数
${url}?type=page&pageNumber=0&pageSize=2
commentId
親コメントのID。指定されたコメントへの返信を取得
${url}?type=reply&commentId=1&loadedCount=0
loadedCount
取得済みの返信数(動的読み込み時に使用)
${url}?type=reply&commentId=1&loadedCount=0
url
リンクからコメントを取得する場合のURL
${url}?type=link&url=xxxxx
ユーザー情報の要求と応答
メンション機能を使用する場合、ユーザーIDまたはユーザー名を検索し、以下の操作を行います。
操作
要求の種類
要求データ
応答データ
読み込み
GET
ユーザーID
一致したユーザー情報
読み込み
GET
ユーザー名
一致したユーザー情報の配列
リアクション情報の要求と応答
コメントに対してリアクションの表示、追加、削除を行う場合、各操作を伴うリクエストが開始され、サーバーに送信されます。
操作
要求の種類
要求データ
応答データ
読み込み
GET
コメントIDとユーザーID
リアクションの配列
追加
POST
リアクションが追加されたコメント(userId, commentId, reactChar)
操作の成功または失敗
削除
DELETE
リアクションが削除されたコメント(userId, commentId, reactChar)
操作の成功または失敗
コンテンツタイプの指定
remoteプロパティのrequestDataTypeを使用して、HTTP通信においてPOST/PUTリクエストに送信されるデータの形式を指定できます。デフォルトではGcCommentRequestDataType.FormDataです。
GcCommentRequestDataTypeの値
説明
JSON
JSON形式
FormData
フォームデータ形式
import "./styles.css";
import "@mescius/inputman.comment/CSS/gc.inputman.comment.css";
import "@mescius/inputman.richtexteditor/CSS/gc.inputman.richtexteditor.css";
import { InputMan } from "@mescius/inputman.comment";
import * as RichTextEditorControl from "@mescius/inputman.richtexteditor";
import "@mescius/inputman.richtexteditor/JS/plugins/all";
const baseURL = window.location.origin + '/inputmanjs/demos/server/api/DataSource';
const loadReactionCount = async () => {
const response = await fetch(`${baseURL}/reactions/count`);
if (response.ok) {
const res = await response.json();
return res.totalReactions;
} else {
return 0;
}
};
const loadCommentCount = async () => {
const response = await fetch(`${baseURL}/comments/count`);
if (response.ok) {
return (await response.json()).totalComments;
} else {
return 0;
}
};
const gcComment = new InputMan.GcComment(document.getElementById('gcComment'), {
openLinkMode: InputMan.OpenLinkMode.GoToComment,
userInfo: {
id: '1',
name: '森上 偉久馬',
avatar: '$IMDEMOROOT$/ja/samples/comment/datasource/img/avatar1.png',
},
header: [
[
{
name: InputMan.GcCommentHeaderFooterItem.Search,
align: 'right',
},
{
name: InputMan.GcCommentHeaderFooterItem.Sort,
align: 'left',
},
],
[
InputMan.GcCommentHeaderFooterItem.CommentCount,
InputMan.GcCommentHeaderFooterItem.ReactionCount
],
],
editorConfig: {
editorType: InputMan.GcCommentEditorType.GcRichTextEditor,
plugins: [RichTextEditorControl.InputMan.GcRichTextEditorPluginItem.All],
height: 150,
menubar: [],
toolbar: [
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Emoticons,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.BlockQuote,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.SeparateLine,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.FontFamily,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.FontSize,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.SeparateLine,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Bold,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Italic,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Strikethrough,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Underline,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.SeparateLine,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Paste,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.PasteText,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.SeparateLine,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.HTMLCode,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.CharMap,
RichTextEditorControl.InputMan.GcRichTextEditorToolbarItem.Link,
]
},
dataSource: {
enabled: true,
remote: {
comments: {
read: {
url: `${baseURL}/comments`,
},
create: {
url: `${baseURL}/comments`,
},
update: {
url: `${baseURL}/comments`,
},
delete: {
url: `${baseURL}/comments`,
}
},
users: {
read: {
url: `${baseURL}/users`,
schema: {
dataSchema: {
name: 'username'
}
}
}
},
reactions: {
read: {
url: `${baseURL}/reactions`,
},
create: {
url: `${baseURL}/reactions`,
},
delete: {
url: `${baseURL}/reactions`,
}
}
}
},
loadReactionCount,
loadCommentCount,
loadUsersInfoHandler: async (context) => {
let users = [];
const response = await fetch(`${baseURL}/users`);
if (response.ok) {
users = await response.json();
if (context.loadType === InputMan.LoadUserType.FilterText) {
return users.filter((u) => u.name.includes(context.value));
} else if (context.loadType === InputMan.LoadUserType.ById) {
return users.filter((u) => u.id === context.value);
}
} else {
console.error('ユーザー情報の読み込みが失敗しました:', response.statusText);
}
}
});
const UpdateCount = async () => {
const commentCount = await loadCommentCount();
const reactionCount = await loadReactionCount();
gcComment.execCommand(InputMan.GcCommentCommand.UpdateCommentCount, { count: commentCount });
gcComment.execCommand(InputMan.GcCommentCommand.UpdateReactionCount, { count: reactionCount });
}
gcComment.addEventListener(InputMan.GcCommentEvent.AfterExecuteCommand, (args) => {
if (args.command === InputMan.GcCommentCommand.PostComment || args.command === InputMan.GcCommentCommand.UpdateComment) {
const mentionInfo = args.value.mentionInfo;
if (Array.isArray(mentionInfo) && mentionInfo.length > 0) {
const nameList = mentionInfo.map(i => i.userName);
alert(`${nameList.join(",")} 宛に、コメントを確認するようメールを送信しました。`);
}
}
const needUpdateCountCommands = [
InputMan.GcCommentCommand.DeleteComment,
InputMan.GcCommentCommand.PostComment,
InputMan.GcCommentCommand.ReplyToComment,
InputMan.GcCommentCommand.Clear,
InputMan.GcCommentCommand.SetComments,
InputMan.GcCommentCommand.ReactToComment,
];
if (needUpdateCountCommands.includes(args.command)) {
UpdateCount();
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="description" content="コメントコンポーネント 使い方" lang="ja" xml:lang="ja" />
<title>コメントコンポーネント - データソース</title>
<!-- SystemJS -->
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
window.onload = function () {
System.import('./src/app');
}
</script>
</head>
<body>
<div id="gcComment"></div>
</body>
</html>
body {
box-sizing: content-box !important;
height: 551px !important;
}
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true
},
meta: {
'*.css': { loader: 'css' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'@mescius/inputman': 'npm:@mescius/inputman/index.js',
'@mescius/inputman/CSS': 'npm:@mescius/inputman/CSS',
'@mescius/inputman.richtexteditor': 'npm:@mescius/inputman.richtexteditor/index.js',
"@mescius/inputman.richtexteditor/CSS": "npm:@mescius/inputman.richtexteditor/CSS",
'@mescius/inputman.richtexteditor/JS/plugins/advlist': 'npm:@mescius/inputman.richtexteditor/JS/plugins/advlist/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/all': 'npm:@mescius/inputman.richtexteditor/JS/plugins/all/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/autosave': 'npm:@mescius/inputman.richtexteditor/JS/plugins/autosave/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/charmap': 'npm:@mescius/inputman.richtexteditor/JS/plugins/charmap/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/directionality': 'npm:@mescius/inputman.richtexteditor/JS/plugins/directionality/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/emoticons': 'npm:@mescius/inputman.richtexteditor/JS/plugins/emoticons/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/fullscreen': 'npm:@mescius/inputman.richtexteditor/JS/plugins/fullscreen/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/htmlcode': 'npm:@mescius/inputman.richtexteditor/JS/plugins/htmlcode/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/image': 'npm:@mescius/inputman.richtexteditor/JS/plugins/image/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/link': 'npm:@mescius/inputman.richtexteditor/JS/plugins/link/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/lists': 'npm:@mescius/inputman.richtexteditor/JS/plugins/lists/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/media': 'npm:@mescius/inputman.richtexteditor/JS/plugins/media/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/pagebreak': 'npm:@mescius/inputman.richtexteditor/JS/plugins/pagebreak/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/preview': 'npm:@mescius/inputman.richtexteditor/JS/plugins/preview/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/save': 'npm:@mescius/inputman.richtexteditor/JS/plugins/save/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/searchreplace': 'npm:@mescius/inputman.richtexteditor/JS/plugins/searchreplace/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/table': 'npm:@mescius/inputman.richtexteditor/JS/plugins/table/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/template': 'npm:@mescius/inputman.richtexteditor/JS/plugins/template/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/wordcount': 'npm:@mescius/inputman.richtexteditor/JS/plugins/wordcount/plugin.js',
'@mescius/inputman.comment': 'npm:@mescius/inputman.comment/index.js',
'@mescius/inputman.comment/CSS': 'npm:@mescius/inputman.comment/CSS',
'@mescius/wijmo': 'npm:@mescius/wijmo/index.js',
'@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles',
'@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures',
'@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js',
'@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js',
'@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js',
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@mescius/spread-sheets-resources-ja': 'npm:@mescius/spread-sheets-resources-ja/index.js',
'@mescius/spread-sheets/styles': 'npm:@mescius/spread-sheets/styles',
'css': 'npm:systemjs-plugin-css/css.js',
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel/systemjs-babel-browser.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
src: {
defaultExtension: 'js'
},
"node_modules": {
defaultExtension: 'js'
},
}
});