1

Here is a DisplayComment function. I want to make a comment thread. Basically a reply structure kind of thing. Each parent comment is displayed in the table and I want to render its replies just below it using the same function and so on for their replies. Screenshot of Display of parent comment

function DisplayComment(commentStruct, identity) {
  const comment = commentStruct.comment;
  return {
    oninit: (v) => {
      console.log(comment.mComment);
    },
    view: (v) => [
      m('tr', [
        m('i.fas.fa-angle-right', {
          class: 'fa-rotate-' + (commentStruct.showReplies ? '90' : '0'),
          style: 'margin-top:12px',
          onclick: () => {
            commentStruct.showReplies = !commentStruct.showReplies;
            // console.log(commentStruct.showReplies);
          },
        }),

        m('td', comment.mComment),
        m(
          'select[id=options]',
          {
            onchange: (e) => {
              if (e.target.selectedIndex === 1) {
                // reply
                util.popupMessage(
                  m(AddComment, {
                    parent_comment: comment.mComment,
                    channelId: comment.mMeta.mGroupId,
                    authorId: identity,
                    threadId: comment.mMeta.mThreadId,
                    parentId: comment.mMeta.mMsgId,
                  })
                );
              } else if (e.target.selectedIndex === 2) {
                // voteUP
                AddVote(
                  util.GXS_VOTE_UP,
                  comment.mMeta.mGroupId,
                  comment.mMeta.mThreadId,
                  identity,
                  comment.mMeta.mMsgId
                );
              } else if (e.target.selectedIndex === 3) {
                AddVote(
                  util.GXS_VOTE_DOWN,
                  comment.mMeta.mGroupId,
                  comment.mMeta.mThreadId,
                  identity,
                  comment.mMeta.mMsgId
                );
              }
            },
          },
          [
            m('option[hidden][selected]', 'Options'),
            util.optionSelect.opts.map((option) =>
              m('option', { value: option }, option.toLocaleString())
            ),
          ]
        ),
        m('td', rs.userList.userMap[comment.mMeta.mAuthorId]),
        m(
          'td',
          typeof comment.mMeta.mPublishTs === 'object'
            ? new Date(comment.mMeta.mPublishTs.xint64 * 1000).toLocaleString()
            : 'undefined'
        ),
        m('td', comment.mScore),
        m('td', comment.mUpVotes),
        m('td', comment.mDownVotes),
      ]),
      commentStruct.showReplies
        ? Data.ParentCommentMap[comment.mMeta.mMsgId]
          ? Data.ParentCommentMap[comment.mMeta.mMsgId].forEach(
              (value) =>
                Data.Comments[value.mMeta.mThreadId] &&
                Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId]
                  ?
                      m(DisplayComment(
                        Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId],
                        identity
                      ))
                  : ''
            )
          : ''
        : '',
    ],
  };
}

I want to know why the recursive call to DisplayComment is not being rendered on the screen in the table below the parent.

Barney
  • 16,181
  • 5
  • 62
  • 76

2 Answers2

1

The answer may not be straightforward – I recommend visiting the Mithril chat room if this answer doesn't solve your problem, where I will be happy to assist in detail.

My first guess is that the logical condition at the bottom of your code may not be resolving. We can simplify this code as follows, replacing ternary operations with simpler 'and' (Mithril treats false as an empty node)

         commentStruct.showReplies
      && Data.ParentCommentMap[comment.mMeta.mMsgId]
      && Data.ParentCommentMap[comment.mMeta.mMsgId].forEach(
          (value) =>
               Data.Comments[value.mMeta.mThreadId]
            && Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId]
            && m(DisplayComment({
                 commentStruct: Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId],
                 identity,
               }))

I have also rewritten the calling signature: commentStruct & identity are now named attributes instead of sequential children (that method should only be used for passing in plain undifferentiated vnodes). This change needs to be persisted to the component functions signature; it is also advisable to query these from the lifecycle function they are invoked in – the view, instead of the closure. It may not matter for this particular component but attributes can change over time, and this method ensures you are always referencing the latest values & not the initial values.

function DisplayComment() {
  return {
    oninit: ({attrs: {commentStruct: {comment}}}) => {
      console.log(comment.mComment);
    },

    view: ({attrs: {commentStruct, identity}}) => {
      const {comment} = commentStruct;

      return [
        m('tr', [
          m('i.fas.fa-angle-right', {
            class: 'fa-rotate-' + (commentStruct.showReplies ? '90' : '0'),
            style: 'margin-top:12px',
            onclick: () => {
              commentStruct.showReplies = !commentStruct.showReplies;
              // console.log(commentStruct.showReplies);
            },
          }),

          m('td', comment.mComment),
          
          m(
            'select[id=options]',
            {
              onchange: (e) => {
                if (e.target.selectedIndex === 1) {
                  // reply
                  util.popupMessage(
                    m(AddComment, {
                      parent_comment: comment.mComment,
                      channelId: comment.mMeta.mGroupId,
                      authorId: identity,
                      threadId: comment.mMeta.mThreadId,
                      parentId: comment.mMeta.mMsgId,
                    })
                  );
                } else if (e.target.selectedIndex === 2) {
                  // voteUP
                  AddVote(
                    util.GXS_VOTE_UP,
                    comment.mMeta.mGroupId,
                    comment.mMeta.mThreadId,
                    identity,
                    comment.mMeta.mMsgId
                  );
                } else if (e.target.selectedIndex === 3) {
                  AddVote(
                    util.GXS_VOTE_DOWN,
                    comment.mMeta.mGroupId,
                    comment.mMeta.mThreadId,
                    identity,
                    comment.mMeta.mMsgId
                  );
                }
              },
            },
            [
              m('option[hidden][selected]', 'Options'),
              util.optionSelect.opts.map((option) =>
                m('option', { value: option }, option.toLocaleString())
              ),
            ]
          ),
          m('td', rs.userList.userMap[comment.mMeta.mAuthorId]),
          m(
            'td',
            typeof comment.mMeta.mPublishTs === 'object'
              ? new Date(comment.mMeta.mPublishTs.xint64 * 1000).toLocaleString()
              : 'undefined'
          ),
          m('td', comment.mScore),
          m('td', comment.mUpVotes),
          m('td', comment.mDownVotes),
        ]),
          commentStruct.showReplies
        && Data.ParentCommentMap[comment.mMeta.mMsgId]
        && Data.ParentCommentMap[comment.mMeta.mMsgId].forEach(
            (value) =>
                Data.Comments[value.mMeta.mThreadId]
              && Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId]
              && m(DisplayComment({
                  commentStruct: Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId],
                  identity
                }))
          )
      ]
    },
  };
}
Barney
  • 16,181
  • 5
  • 62
  • 76
  • Thanks a lot for you answer. I also think the answer is not that straightforward. 1. I tried cleaning up the logical condition using &&. The condition is being resolved as I tested out by logging the comment struct in place of calling the method again and it is displaying as expected on the click of the arrow. – Sukhamjot Singh Jul 08 '22 at 13:37
  • `commentStruct.showReplies && Data.ParentCommentMap[comment.mMeta.mMsgId] && Data.ParentCommentMap[comment.mMeta.mMsgId].forEach( (value) => Data.Comments[value.mMeta.mThreadId] && Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId] && console.log(Data.Comments[value.mMeta.mThreadId][value.mMeta.mMsgId])` – Sukhamjot Singh Jul 08 '22 at 13:43
  • Also I tried de-structuring as you suggested and pass in named parameters, however I received some errors. I understand its a good practice to do it, but the first comment is being displayed in the usual manner using sequential params. – Sukhamjot Singh Jul 08 '22 at 13:45
1

forEach() does not return anything. Used a map() function instead of forEach() and the replies were displayed. Huge credits to @Barney

Ahmad
  • 507
  • 1
  • 11
  • 22