Skip to content

Polls

Intro

This documentation describes the structure and behavior of the poll system, including the HTML form, JavaScript handling, and the PHP backend responsible for processing votes.

Integration

Poll form structure

This is an example of how the template for a poll could look like:

html
    <div
        wf-role="composite_poll_article"
        wf-toolbar-position="top"
        wf-allow="-+"
        class="c-poll"
>
    <span class="c-poll__pretitle">{{ 'wf.module.article.poll.pretitle'|trans }}</span>
    <h2
            wf-module="wfed/body_text/module"
            wf-role="poll_title"
            wf-new
            wf-toolbar-position="none"
            wf-cm-text="poll.question"
            wf-not-sortable
            class="c-poll__title"
    ></h2>


    <div class="poll--simple">
        <form :id=`poll-${poll.id}`
              method="post"
              :action="filters.path('wf_cms_poll_vote', {id: poll.id, type: 'json'})"
              :data-poll-id="poll.id"
              :class="{'poll-voted': poll.isExpired}"
              :data-infoUrl="filters.path('wf_cms_polls', {type: 'json'})"
              class="c-poll__form poll poll-form to-hide">

            <div v-for="option in poll.options" :key="option.id" class="poll-form-group to-hide">
                <label :for="`poll-option-${poll.id}-${option.id}`" class="c-poll__form__label">
                    <input type="radio"
                           name="vote"
                           :value="option.id"
                           :id="`poll-option-${poll.id}-${option.id}`"
                           class="c-poll__form__input"
                    >
                    <span class="c-poll__form__checkmark"></span>
                    [[ option.option_name ]]
                </label>

            </div>

            <input type="submit" value="Votar" class="c-poll__button to-hide" data-type="poll-vote-button" >
            <div class="c-detail--poll">
                <div class="c-detail--poll__chart"  v-for="option in poll.options" :key="option.id">
                    <div class="c-detail--poll__chart__item c-detail__poll__item__option"  :data-id="`${option.id}`">
                        <div class="c-detail--poll__chart__txt">
                            [[ option.option_name ]]
                        </div>
                        <div class="c-detail--poll__chart__box">
                            <span class="c-detail--poll__chart__bar c-detail__poll__item__option__bar__progress poll-bar"></span>
                        </div>
                        <span class="c-detail--poll__chart__porcentage c-detail__poll__item__option__bar__progress__percentage"></span>
                        <span class="c-detail--poll__chart__votes">[[ option.vote_count ]] votos</span>
                    </div>
                </div>
                <span class="c-detail--poll__chart__footer">Han votado <strong class="poll-total-votes">[[ poll.total_persons_that_voted ]]</strong> personas</span>
            </div>
        </form>

    </div>

</div>
  • The form uses Vue.js directives to dynamically populate poll options.

  • Each option is a radio button, allowing users to select one choice per poll.

  • The form submits via POST to the wf_cms_poll_vote route, passing the selected option.

JavaScript Poll Handling

IMPORTANT: You have to import "wfcb/amd/poll" (vendor/wfcms/standard/Wf/Bundle/CmsBaseBundle/Resources/public/javascripts/amd/poll.js) in your article.js. This JavaScript file relies on some specific parts for the HTML structure, read below.

Necessary HTML structure for JavaScript Functionality

Main Poll Container:
  • .c-detail__poll: main container for the poll
  • #poll-{id} poll elements must have an id with this pattern
Poll Form and Results:
  • .poll-form: container for the voting form
  • .poll-results: container for the voting results
  • .multiple: for multiple-choice polls
  • .c-detail__poll__footer__link: link to toggle between form and results
Poll Options:
  • .c-detail__poll__item__option: individual poll option, must have :data-id="{option.id}"
  • .c-detail__poll__item__option__bar__progress: progress bar for the results
  • .c-detail__poll__item__option__bar__progress__percentage: percentage inside the progress bar
  • .poll-option-votes: displays the number of votes per option
Total Poll Votes:
  • .poll-total-votes: displays the total number of votes
Disabled or Already Voted Polls:
  • .poll-voted: applied to polls that the user has already voted
Control and Processing Classes:
  • .generated: used internally to mark elements that have been processed in order to avoid duplicate events

  • On submission, it prevents the default form submission and validates the selection.

  • If an option is selected, it sends an AJAX request to the form action URL. Then a JSON is returned and a Cookie is set with _cms-poll-{pollId} to show the poll data

NOTE: add the css necessary to show/hide poll options or results, in this example we used:

scss
.c-detail--poll {
    display: none;
}
.poll-voted {
    .to-hide {
        display: none;
    }
    .c-detail--poll {
        display: block;
    }
}

PHP Backend (Vote Processing)

The Wf\Bundle\CmsBaseBundle\Controller\PollController::postPollVoteAction (route id: wf_cms_poll_vote, used in the module template above) processes votes and returns a JSON response or renders a Twig template (JSON if you use the example) also add a cookie to the document to prevent a user can vote several times