Flash message doesn't get rendered in plugin

Hi,

I got a plugin which let user write and publish comments on my page. I’d like to inform them via flash messages, that their comment has been created. My problem is, that the plugin gets rendered twice, the flash message container therefore flushed and nothing is rendered in the actual output.

My code


CommentController

<?php

namespace V\S\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Error\Messages;
use Neos\Neos\Service\LinkingService;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use V\S\Service\CommentService;
use V\S\Domain\Model\UserComment;
use Psr\Log\LoggerInterface;

class CommentController extends ActionController
{

	/**
	 * @Flow\Inject
	 * @var LoggerInterface
	 */
	protected $logger;

	/**
	 * @var LinkingService
	 * @Flow\Inject
	 */
	protected $linkingService;

	/**
	 * @Flow\Inject
	 * @var CommentService
	 */
	protected $commentService;

	/**
	 * Shows the form to create a comment
	 *
	 * @return void
	 */
	public function showFormAction()
	{
		$this->logger->debug('--------------------');
		$this->logger->debug('preparing to render form');

		# Assign node data to Fluid
		#--------------------------
		$this->view->assign('documentNode', $this->request->getInternalArgument('__documentNode'));
		$this->view->assign('node', $this->request->getInternalArgument('__node'));

		# Assign properties of the fusion fluidData array to Fluid
		#---------------------------------------------------------
		if (is_array($this->request->getInternalArgument('__fluidData'))) {
			foreach ($this->request->getInternalArgument('__fluidData') as $key => $value) {
				$this->view->assign($key, $value);
			}
		}
	}

	/**
	 * Creates a new comment
	 *
	 * @param UserComment $comment Model holding the comment data
	 * @return void
	 */
	public function newCommentAction(UserComment $comment)
	{
		$this->logger->debug('------------------------');
		$this->logger->debug('preparing to add comment');


		# Create the new comment Node
		#----------------------------
		// @var Node A ContentCollection node containing the Comment nodes
		$feedNode = $this->request->getInternalArgument('__feed');

		$commentNode = $this->commentService->create($comment, $feedNode);
		$this->addFlashMessage('Comment successfully created', '', Messages\Message::SEVERITY_OK, array(), 1498337705);

		# Set the page anchor for redirection the user
		#---------------------------------------------
		$pageAnchor = $commentNode->getName();

		# If moderation is enabled add a flash message and change the page anchor
		#------------------------------------------------------------------------
		if ($this->settings['moderation']['isEnabled']) {
			$this->addFlashMessage('Comments are moderated. Your comment appears after it has been approved.', '', Messages\Message::SEVERITY_NOTICE, array(), 1498337995);
			$pageAnchor = $this->settings['flashMessageAnchor'];
		}

		# Send signal and redirect user
		#------------------------------
		$this->logger->debug('added comment');
		$this->emitCommentCreated($commentNode, $feedNode);

		$this->logger->debug('ready to redirect');
		$this->redirectToNewComment($pageAnchor);
		$this->logger->debug('redirection done');
	}

	/**
	 * Redirects the comment creator to the documentNode which contains the new
	 * comment instead of the destination defined in Routes.yaml (which renders
	 * basically nothing).
	 *
	 * @var string $anchor the section / page anchor of the newly created comment
	 */
	protected function redirectToNewComment($anchor)
	{
		$this->logger->debug('---------------------');
		$this->logger->debug('preparing redirecting');
		$documentNode = $this->request->getInternalArgument('__documentNode');
		$uri = $this->linkingService->createNodeUri(
			$this->controllerContext,
			$documentNode,
			$documentNode->getContext()->getRootNode(),
			'html',
			TRUE,
			array(),
			$anchor
		);

		$this->logger->debug('start redirecting');
		$this->redirectToUri($uri, 0, 201);
		$this->logger->debug('redirected');
	}

	/**
	 * Creates an error message if no comment has been created.
	 *
	 * @return Messages\Message The flash message or FALSE if no flash message should be set
	 */
	protected function getErrorFlashMessage()
	{
		return new Messages\Error('Your comment has not been created.', 1498339181);
	}

	/**
	 * Signal which informs about a newly created comment
	 *
	 * @param NodeInterface $commentNode The created comment node
	 * @return void
	 * @Flow\Signal
	 */
	protected function emitCommentCreated(NodeInterface $commentNode, NodeInterface $feedNode)
	{
	}
}

Fusion plugin

prototype(V.S:Component.CommentFeed.Part.NewCommentForm) < prototype(Neos.Neos:Plugin) {
	package = 'V.S'
	controller = 'Comment'
	action = 'showForm'

	documentNode = ${documentNode}
	// @var Node The ContentCollection node which holds all the comments
	feed = ${q(node).find('feed').get(0)}
	// @var array Array holding properties which will be passed to the Fusion template
	fluidData = Neos.Fusion:DataStructure {
		// @var string Anchor within the document.
		flashMessageAnchor = ${Configuration.setting('V.S.flashMessageAnchor')}
	}
}

Fusion component

prototype(V.S:Component.CommentFeed) < prototype(Neos.Neos:ContentComponent) {

	##### API #####
	commentFeedNodePath = 'feed'
	noCommentsHint = V.S:Component.CommentFeed.Part.NoCommentsHint
	##### API #####

	renderer = Neos.Fusion:Join {
		commentFeed = Neos.Neos:ContentCollection {
			nodePath = ${props.commentFeedNodePath}

			attributes.class = 'comment-feed'

			// Don't render an empty comment feed. Render a hint instead
			@process.showNoCommentHint = ${props.noCommentsHint}
			// NOTE: `node` is set to the ContentCollection by the CC prototype
			@process.showNoCommentHint.@if.hasNoComments = ${!q(node).children().is()}
		}

		createCommentForm = V.S:Component.CommentFeed.Part.NewCommentForm
	}
}

ShowFormAction.html

<div id="{flashMessageAnchor}">
	<f:flashMessages />
</div>

<f:form class="form{f:validation.ifHasErrors(then:'--error')}" action="newComment" objectName="comment" id="newCommentForm" object="{comment}">

	<f:validation.results>
		<f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath">
			<div class="ui message">
				<div class="header">{propertyPath}</div>
				<ul class="list">
				<f:for each="{errors}" as="error">
					<li>{error.code}: {error}</li>
				</f:for>
				</ul>
			</div>
		</f:for>
	</f:validation.results>

	<div class="two fields">
		<div class="field{f:validation.ifHasErrors(for: 'comment.authorName', then:' error')}">
			<label for="authorName">Name</label>
			<f:form.textfield property="authorName" id="authorName" placeholder="Name"/>
		</div>

		<div class="field{f:validation.ifHasErrors(for: 'comment.authorEmail', then:' error')}">
			<label for="authorEmail">E-Mail</label>
			<f:form.textfield property="authorEmail" id="authorEmail" placeholder="E-Mail"/>
		</div>
	</div>

	<div class="field{f:validation.ifHasErrors(for: 'comment.content', then:' error')}">
		<f:form.textarea property="content" id="content" />
	</div>
	<f:form.hidden value="{documentNode}" name="documentNode" />


	<f:form.button type="submit" class=" button">
		<i class="icon edit"></i> Submit
	</f:form.button>

</f:form>

The Log output

# Browsing to the page with the comment form
20-02-06 10:07:03 22439      DEBUG                          Session: Resumed session with id Lf1ZpSMZNYGylKxL9yYYlxhltjPz6JIq which was inactive for 14 seconds. (14s)
20-02-06 10:07:03 22439      DEBUG                          Router route(): A cached Route with the cache identifier "f44c46f2a113d29a592e89f44798d2b4" matched the request "http://mysite.local/entwicklung/paradigmen.html (GET)".
20-02-06 10:07:03 22439      DEBUG                          CSRF: No token required, safe request
20-02-06 10:07:03 22439      DEBUG                          CSRF: No token required, safe request
20-02-06 10:07:03 22439      DEBUG                          --------------------
20-02-06 10:07:03 22439      DEBUG                          preparing to render form
# Entered data into form and submitted form
20-02-06 10:07:09 22439      DEBUG                          Session: Resumed session with id Lf1ZpSMZNYGylKxL9yYYlxhltjPz6JIq which was inactive for 6 seconds. (6s)
20-02-06 10:07:09 22439      DEBUG                          Router route(): A cached Route with the cache identifier "41939226b7fc1217a81c2c591b491457" matched the request "http://mysite.local/entwicklung/paradigmen.html?--v_s-component_commentfeed%5B%40package%5D=v.s&--v_s-component_commentfeed%5B%40controller%5D=comment&--v_s-component_commentfeed%5B%40action%5D=newcomment&--v_s-component_commentfeed%5B%40format%5D=html (POST)".
20-02-06 10:07:09 22439      DEBUG                          CSRF: No token required, method Neos\Neos\Controller\Frontend\NodeController::showAction() is tagged with a "skipcsrfprotection" annotation
20-02-06 10:07:09 22439      DEBUG                          CSRF: Successfully verified token for V\S\Controller\CommentController::newCommentAction()
20-02-06 10:07:09 22439      DEBUG                          ------------------------
20-02-06 10:07:09 22439      DEBUG                          preparing to add comment
20-02-06 10:07:09 22439      DEBUG                          added comment
20-02-06 10:07:09 22439      DEBUG                          ready to redirect
20-02-06 10:07:09 22439      DEBUG                          ---------------------
20-02-06 10:07:09 22439      DEBUG                          preparing redirecting
# the controller has done its job and is redirecting to show the form again
20-02-06 10:07:09 22439      DEBUG                          start redirecting
20-02-06 10:07:09 22439      DEBUG                          Content cache: Removed 1 entries which were tagged with "DescendantOf_%d0dbe915091d400bd8ee7f27f0791303%_f4752a52-780a-6fb3-cbb3-f53827751f49" because node "/sites/v-s/node-qrej3nl0jc4pp/node-2bc07q8nucbjh/commentfeed/feed/comment-5e3be54d6896d" has changed.
20-02-06 10:07:09 22439      DEBUG                          Content cache: Removed 1 entries which were tagged with "DescendantOf_%d0dbe915091d400bd8ee7f27f0791303%_9b8568c8-217f-4078-8179-9e9cefb4f826" because node "/sites/v-s/node-qrej3nl0jc4pp/node-2bc07q8nucbjh/commentfeed/feed/comment-5e3be54d6896d" has changed.
20-02-06 10:07:09 22439      DEBUG                          Session: Resumed session with id Lf1ZpSMZNYGylKxL9yYYlxhltjPz6JIq which was inactive for 0 seconds. (0s)
20-02-06 10:07:09 22439      DEBUG                          Router route(): A cached Route with the cache identifier "f44c46f2a113d29a592e89f44798d2b4" matched the request "http://mysite.local/entwicklung/paradigmen.html (GET)".
20-02-06 10:07:09 22439      DEBUG                          CSRF: No token required, safe request
20-02-06 10:07:10 22439      DEBUG                          CSRF: No token required, safe request
# showFormAction() gets called the first time.
20-02-06 10:07:10 22439      DEBUG                          --------------------
20-02-06 10:07:10 22439      DEBUG                          preparing to render form
20-02-06 10:07:10 22439      DEBUG                          CSRF: No token required, safe request
# showFormAction() gets called the second time.
20-02-06 10:07:10 22439      DEBUG                          --------------------
20-02-06 10:07:10 22439      DEBUG                          preparing to render form

The last lines are the important ones.

For some reason the showFormAction gets called twice. Since the action gets called twice, the flash messages container gets flushed during the first render attempt and the flash messages rendered—just as desired.
But as the action gets called a second time, the first rendering gets overridden with the rendering result of the second showFormAction call. In the second rendering attempt there is no data in the flash message container and therefore no flash message to render.

The main question is: Why is the action rendered twice after creating the comment and doing the redirect?

Side notes:

  • There are no other <f:flashMessages /> VH calls somewhere on the page
  • The plugin is only invoked once on the page