How to reset phx-click

Have created a simple liveview app that is a timer. Two buttons one that waits for 8 minutes and another that waits for 30 minutes then plays an mp3. I would like to be able to click the same button again after the timer is finished but I don’t know how to reset it. I can click the other button either during the timer countdown or after the timer goes off but I can’t click the same button a second time. Hopefully this is something simple. I can provide code if it helps.

Thanks
Brett

You’re going to need to show the code.

Are you not receiving the event on the back end when you click the button?

1 Like

I get the first one fine. An event is triggered on the server which changes a couple of values on hidden inputs on the page and liveview refreshes. I catch an event in JS on the browser, read the hidden values and trigger a timer that counts down seconds and moves a progress bar. At this point I can re-click the same button and it does nothing which is fine at this point. But when the progress bar gets to zero I would like to re-activate the button. There are two buttons one for 8 minutes and one for 30. The other button stays active and if I click it it goes to the server and resets the timer to the new countdown and that button goes inactive and the first one becomes active again. The thing is, if the timer isn’t running I want both timed options to be available.

Here is the sequence of code being run:

Browser:

  <span id="bar" style={@bar}>
     <%= if (@seconds==0), do: "", else: "#{@seconds} secs" %>
  </span>

  <button phx-click= "on8"> 8 min</button>
  <button phx-click="on30">30 min</button>

  <input id="max"  type="hidden" value={@max}>
  <input id="secs" type="hidden" value={@seconds}>

Server:


  def handle_event("on8", _, socket) do
    seconds = 8 * 60
    bar     = "width:100%"
    socket  = assign(socket, %{bar: bar, seconds: seconds, max: seconds})
    {:noreply, socket}
  end

Browser:

let liveSocket = new LiveSocket("/live", Socket, {
  params: { _csrf_token: csrfToken },
  hooks: hooks,
  dom: {
    onBeforeElUpdated(from, to) {
############# catching the update here to trigger the timer #####################
      if (from.id=="max") {
        if (to.value>1) {
          start_timer();
        }
      }
#####################################
      if (from._x_dataStack) {
        window.Alpine.clone(from, to);
      }
    },
  },
});

function start_timer() {
  if (! timer_running) {
    timer_running = true;
    setTimeout(process_timer, 1000);
  }
}

function process_timer() {
  max     = document.getElementById('max').value;
  seconds = document.getElementById('secs').value;
  seconds = seconds - 1;
  if (seconds<0) {
    seconds = 0;
  }
  document.getElementById('secs').value = seconds;
  var bar = document.getElementById('bar');
  if (seconds==0) {
    bar.innerHTML= "";
  } else {
    bar.innerHTML= seconds + " secs";
  }
  percent = Math.trunc((seconds * 100.0) / max);
  bar.style.width = percent + "%";
  if (seconds>0) {
    setTimeout(process_timer, 1000);
  } else {
    timer_running = false;
    play_sound();
###################### I WISH THIS WORKED! *****************
    eval("Push.reset()");
######################I'd like the 8minute button to be re-activated *****************
  }
}

You can see my attempt at trying to reach into the liveview code and call some kind of reset to re-activate the button but it doesn’t work.

I hope this is enough for you to see what I am trying to do.

Surely appreciate any help I can get as am new at Elixir/Phoenix although have been coding for 40+ years.

Brett

Why are you writing the code in the dom section and not using a hook?

If you want to push an event from the frontend to the backend, you can create a hook and use pushEvent.

You might consider putting values secs and max as data attributes, i.e data-secs.

1 Like

Why don’t you move most of the business logic to Elixir - keep track of the click timestamps and progress in the socket state and implement a basic state machine to react to clicks depending on the time/progress.

1 Like

cmo → Am unaware of the hooks aside from the one am using. I’ll look for some doc on this… since a pushEvent can send data, maybe this is the way to do it…

The buttons will eventually populate data rows on the backend everytime they are clicked so perhaps a solution could be to make them submit buttons on a form. There will be a text field and a text/combo that will have data which will be sent back with the click. A timer runs on the browser to operate the progress bar.

bartblast → The only business logic (at this stage of the project) really is the storing of a data row on each button click. I’d still have the same problem though, when the button is clicked it is deactivated and I’d need someway to reactivate it so it can be clicked once the time has elapsed.

Thanks for this help, I’ll keep playing with it today. It seems ovbious now that there are better ways to accomplish what am after.

Forehead slap. The button was not updating the page because bar & seconds had not changed (at least on the server). No state change, no need to refresh the page. The state is changing in the browser but it is only there for the display logic without any need to store something on the server after the click is sent…

When I’m actually storing data, timestamps etc. I can have more sophisticated logic about elapsed time working in parallel with what is going on the front end. But at least I’m moving ahead… I did change to using a real “hook” hooking to the updated event on a hidden input which worked great, getting rid of the DOM kluge!

blizz

1 Like