Выполнение сценария обратного отсчета JavaScript несколько раз внутри цикла - PullRequest
1 голос
/ 05 мая 2019

Я использую Symfony и в настоящее время пытаюсь показать эти элементы из базы данных, но здесь есть поворот, поскольку каждый из этих элементов имеет длительность, и мне нужно показать обратный отсчет для этой продолжительности.

Я уже реализовал скрипт обратного отсчета (хотя у него все еще есть несколько проблем), но он выполняется только с первым значением, в то время как поля в других строках остаются пустыми.

Сейчас я объясню код: у меня много парковок, у каждой парковки есть много автомобилей (голосов): на моей странице показаны парковки, связанные с ними машины, а для каждого автомобиля - их номер и отсчет времени, когда этот автомобиль разрешено (время прописано в столбце в базе данных).

Я также использую файлы cookie в своем сценарии, чтобы сэкономить минуты и секунды, поэтому я не знаю, как это сделать для нескольких значений. вот фотография того, что я сделал:

enter image description here

вот мой код (извините, это грязно):

{% extends 'Agent/Baseagent.html.twig' %}

{% block title %}Parking index{% endblock %}

{% block body %}

    {% for parking in user.parkings %}
         <h2>Parking</h2>
             {{ parking.libelle }}

              <h2>voitures</h2>
<table id="file_export" class="table table-striped table-bordered">
                <thead>

                <tbody>
            {% for voitures in parking.voitures %}
                <tr>
                <td>
               {{ voitures.matricule }}
                </td>


                <td>
                <div id="timer" class="js-user-rating" data-is-test="{{ voitures.time}}"></div>
                <td class="center"><span id="demo"></span></td>

                 <script>
 var firstTime = true;

function countdown(minutes) {
    var seconds = 60;
    var mins = minutes;

    if(firstTime && getCookie("minutes")&&getCookie("seconds"))
    {
            firstTime = false;

         var seconds = getCookie("seconds");
         var mins = getCookie("minutes");
    }

    function tick() {

        var counter = document.getElementById("timer");
        setCookie("minutes",mins,10)
        setCookie("seconds",seconds,10)
        var current_minutes = mins-1
        seconds--;
        counter.innerHTML = 
        current_minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
        //save the time in cookie



        if( seconds > 0 ) {
            setTimeout(tick, 1000);
        } else {

            if(mins > 1){

               // countdown(mins-1);   never reach “00″ issue solved:Contributed by Victor Streithorst    
               setTimeout(function () { countdown(mins - 1); }, 1000);

            }
        }
    }
    tick();
}
function setCookie(cname,cvalue,exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires=" + d.toGMTString();
    document.cookie = cname+"="+cvalue+"; "+expires;
}
 function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}
countdown(55);
                 </script>

                </td>
                </tr>
             {% endfor %}
        {% else %}
            <tr>
                <td colspan="6">no records found</td>
            </tr>
        {% endfor %}
                </tbody>
            </table>{% endblock %}


My Voiture Entity

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\VoitureRepository")
 */
class Voiture
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=200)
     */
    private $matricule;

    /**
     * @ORM\Column(type="datetime")
     */
    private $gareele;



    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Parking", inversedBy="voitures")
     * @ORM\JoinColumn(nullable=false)
     */
    private $parking;

    /**
     * @ORM\Column(type="boolean")
     */
    private $parked;

    /**
     * @ORM\Column(type="integer")
     */
    private $time;



    public function getId(): ?int
    {
        return $this->id;
    }

    public function getMatricule(): ?string
    {
        return $this->matricule;
    }

    public function setMatricule(string $matricule): self
    {
        $this->matricule = $matricule;

        return $this;
    }

    public function getGareele(): ?\DateTimeInterface
    {
        return $this->gareele;
    }

    public function setGareele(\DateTimeInterface $gareele): self
    {
        $this->gareele = $gareele;

        return $this;
    }



    public function getParking(): ?Parking
    {
        return $this->parking;
    }

    public function setParking(?Parking $parking): self
    {
        $this->parking = $parking;

        return $this;
    }

    public function getParked(): ?bool
    {
        return $this->parked;
    }

    public function setParked(bool $parked): self
    {
        $this->parked = $parked;

        return $this;
    }

    public function getTime(): ?int
    {
        return $this->time;
    }

    public function setTime(int $time): self
    {
        $this->time = $time;

        return $this;
    }


}

и мой контроллер (ничего особенного, просто получите парковки, связанные с подключенным пользователем):

/**
 * @Route("/agent")
 */
class AgentController extends AbstractController
{
    /**
     * @Route("/", name="agent", methods={"GET"})
     */
    public function index(): Response
    {

        $use = $this->get('security.token_storage')->getToken()->getUser();

        $user = $this->getUser();   
        $parkingz=$this->getUser()->getParkings();

        return $this->render('Agent/Agent.html.twig', [
            'user' => $user,
            'parkingz'=>$parkingz,
        ]);
    }

Ответы [ 2 ]

1 голос
/ 08 мая 2019

У вас есть некоторые проблемы с вложением (тэг script должен выходить из цикла for), повторные идентификаторы и неточная система синхронизации.Если вам необходимо обновить данные в реальном времени, используйте временную метку вместе с системными часами, чтобы определить фактическое оставшееся время.

Сначала добавьте метод к своей сущности, чтобы получить метку времени истечения (будет в секундах)

class Voiture
{
  ...

  public function getExpiresAt()
  {
    $gareele = $this->getGareele();
    $expires = clone $gareele;
    $expires->modify('+' . $this->getTime() . ' min');

    return $expires->format('U');
  }
}

Затем в шаблоне измените интервал таймера на class="timer" (идентификатор не требуется) и добавьте атрибут данных с отметкой времени истечения.Скрипт перебирает все .timer s и обновляет текст, чтобы отразить оставшиеся дни, часы, минуты и секунды на данный момент времени.Здесь я обновляю текст каждые 100 миллисекунд, используя setTimeout() внутри функции.

{% extends 'Agent/Baseagent.html.twig' %}

{% block title %}Parking index{% endblock %}

{% block body %}

  {% for parking in user.parkings %}
    <h2>Parking</h2>
    {{ parking.libelle }}

    <h2>voitures</h2>
    <table id="file_export" class="table table-striped table-bordered">
      <thead></thead>

      <tbody>
      {% if parking.voitures|length > 0 %}
        {% for voitures in parking.voitures %}
          <tr>
            <td>
              {{ voitures.matricule }}
            </td>
            <td class="center">
              <span class="timer" data-expires="{{ voitures.getExpiresAt() }}"></span>
            </td>
          </tr>
        {% endfor %}
      {% else %}
        <tr>
          <td colspan="6">no records found</td>
        </tr>
      {% endif %}
      </tbody>
    </table>
  {% endfor %}

  <script>
    var timers = document.querySelectorAll('.timer')

    function updateTimers () {
      var rightNow = Math.floor(Date.now()/1000) // in seconds
      timers.forEach(function (timer) {

        var expires = parseInt(timer.dataset.expires) // in seconds

        if (rightNow > expires) {
          // Time expired
          timer.innerText = 'Expired'
        } else {
          var seconds = expires - rightNow
          var minutes = Math.floor(seconds/60)
          var hours = Math.floor(minutes/60)
          var days = Math.floor(hours/24)
          seconds = ('0' + String(seconds%60)).slice(-2)
          minutes = ('0' + String(minutes%60)).slice(-2)
          hours = ('0' + String(hours%24)).slice(-2)
          timer.innerText = days + 'd ' + hours + 'h ' + minutes + 'm ' + seconds + 's'
        }

      })

      setTimeout(function () {
        updateTimers()
      }, 100)
    }

    updateTimers()
  </script>

{% endblock %}

Примечание

Если вы собираетесь добавить больше таймеровчерез ajax (после загрузки страницы) вы должны поместить строку:

var timers = document.querySelectorAll('.timer')

внутри функционального блока, чтобы искать новые таймеры при каждом вызове.

0 голосов
/ 05 мая 2019

Есть дубликаты идентификаторов ("test", "demo"), по одному на каждый голос.

Из MDN doc :

Глобальный атрибут id определяет уникальный идентификатор (ID), который должен быть уникальный во всем документе. Его цель - идентифицировать элемент при связывании (с использованием идентификатора фрагмента), написании сценариев или стиля (с CSS).

Этот document.getElementById("demo") собирается вернуть один результат, вероятно, первый в DOM. (Я подозреваю, что это также верно здесь var test = $('#test').data("isTest");, но я не владею jQuery).

Возможно, вы могли бы сделать выбор (ы) по атрибуту "name" и изменить код, чтобы иметь один элемент <script>, который проходит через нужные узлы.

...