Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot read property 'getDescendentByEl' of undefined #870

Closed
himrock922 opened this issue May 9, 2020 · 17 comments
Closed

Cannot read property 'getDescendentByEl' of undefined #870

himrock922 opened this issue May 9, 2020 · 17 comments

Comments

@himrock922
Copy link

Environment

  • Elixir version (elixir -v): 1.9.4
  • Phoenix version (mix deps): 1.5.1
  • Phoenix LiveView version (mix deps): master version(commit log: 9d895f8)
  • NodeJS version (node -v): v13.10.1
  • NPM version (npm -v): 6.13.7
  • Operating system: Ubuntu 18.04.03 LTS
  • Browsers you attempted to reproduce this bug on (the more the merrier): Google Chrome(81.0.4044.138(Official Build))
  • Does the problem persist after removing "assets/node_modules" and trying again? Yes/no: yes

Actual behavior

click button on phx-click, console error.

phoenix_live_view.js:820 Uncaught TypeError: Cannot read property 'getDescendentByEl' of undefined
    at e.value (phoenix_live_view.js:820)
    at eval (phoenix_live_view.js:783)
    at W (phoenix_live_view.js:393)
    at e.value (phoenix_live_view.js:782)
    at e.value (phoenix_live_view.js:812)
    at eval (phoenix_live_view.js:991)
    at Object.debounce (phoenix_live_view.js:1269)
    at e.value (phoenix_live_view.js:1135)
    at eval (phoenix_live_view.js:990)
  • capture
    81027296-39055980-8eb8-11ea-8930-fdb13d034063

Expected behavior

If the user click button connected "phx-click", "handle_event" method execute.

app.js

import 'jquery/dist/jquery.slim.min';
import 'fuse.js/dist/fuse';
import 'popper.js/dist/popper.min';
import 'bootstrap/dist/js/bootstrap.min';
import 'bootstrap-select-dropdown/dist/bootstrap-select-dropdown.min';
import "phoenix_html"

import NProgress from "nprogress"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"

$(window).on('load',function(){
  if(document.URL.match("daily_reports/new")) {
    let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
    let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
    
    // Show progress bar on live navigation and form submits
    window.addEventListener("phx:page-loading-start", info => NProgress.start())
    window.addEventListener("phx:page-loading-stop", info => NProgress.done())
    
    // connect if there are any LiveViews on the page
    liveSocket.connect()
    
    // expose liveSocket on window for web console debug logs and latency simulation:
    // >> liveSocket.enableDebug()
    // >> liveSocket.enableLatencySim(1000)
    window.liveSocket = liveSocket
  }
});

new.html.leex

<button phx-click="add_articles" class="btn btn-success">#</button>

new.ex

defmodule WeReportsWeb.DailyReports.New do
    use WeReportsWeb, :live_view
    use Phoenix.LiveView, layout: {WeReportsWeb.LayoutView, "live.html"}
    alias WeReports.UserManager
    require IEx

    @impl true
    def mount(_params, session, socket) do
      {:ok, user} = WeReportsWeb.Live.AuthHelper.load_user(session)
      group_lists = get_user_groups(user.id)
      socket = assign(socket, :current_user, user)
      socket = assign(socket, :groups, group_lists.groups)
      {:ok, assign(socket, query: "", results: %{})}
    end
 
    @impl true
    def handle_event("add_articles", _value, socket) do
      IEx.pry
    end

ref Issue

#748

@chrismccord
Copy link
Member

I cannot recreate this using the provided code. Are you sure you updated your js assets? If you can provide a complete project to reference or more info it would be helpful. It's also possible your IEx.pry is timing out, but things worked as expected on my side. Thanks!

@himrock922
Copy link
Author

himrock922 commented May 12, 2020

@chrismccord

I'm sorry, I did provide some files, but
load_user helper did not exists.

auth_helper.ex (ref ueberauth/guardian_phoenix#6 (comment))

defmodule WeReportsWeb.Live.AuthHelper do
  @moduledoc "Helpers to assist with loading the user from the session into the socket"
  @claims %{"typ" => "access"}
  @token_key "guardian_default_token"
  def load_user(%{@token_key => token}) do
    case Guardian.decode_and_verify(WeReports.UserManager.Guardian, token, @claims) do
      {:ok, claims} ->
        WeReports.UserManager.Guardian.resource_from_claims(claims)
      _ ->
        {:error, :not_authorized}
    end
  end
  def load_user(_), do: {:error, :not_authorized}
end

this file provide user_resource by session via guardian.

First mount callback execute, socket assigns successed.
But, Second mount callback execute fail.

IEx.pry(1st.)

    6:     @impl true
    7:     def mount(_params, session, socket) do
    8:       IEx.pry
    9:       case WeReportsWeb.Live.AuthHelper.load_user(session) do
   10:          {:ok, user} ->

Allow? [Yn] Y

Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> session
%{
  "_csrf_token" => "eWNCiAe5cu0lnAtp9Lkb47DH",
  "guardian_default_token" => "*****"
}

IEX.pry(2nd.)

iex(1)> continue[info] CONNECTED TO Phoenix.LiveView.Socket in 162µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "Pzk2OwcqHRhUJlszLQcgA3cCLRpgATAZZnxxnkx-7Sk_CFTsNNFxT6tQ", "vsn" => "2.0.0"}
Request to pry #PID<0.930.0> at WeReportsWeb.DailyReports.New.mount/3 (lib/we_reports_web/live/daily_reports/new.ex:8)

    6:     @impl true
    7:     def mount(_params, session, socket) do
    8:       IEx.pry
    9:       case WeReportsWeb.Live.AuthHelper.load_user(session) do
   10:          {:ok, user} ->

Allow? [Yn] Y

Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
pry(1)> continue
pry(1)> session
%{}
pry(2)> socket
#Phoenix.LiveView.Socket<
  assigns: %{
    flash: %{},
    live_action: nil,
    live_module: WeReportsWeb.DailyReports.New
  },
  changed: %{},
  endpoint: WeReportsWeb.Endpoint,
  id: "phx-Fg5MOkNNbRGb6wrB",
  parent_pid: nil,
  root_pid: #PID<0.930.0>,
  router: WeReportsWeb.Router,
  view: WeReportsWeb.DailyReports.New,
  ...
>

mount callback is execute twice...?

@rockwood
Copy link

I'm seeing this as well. My mount callback is executed twice. When phx events fire before the second mount call, I get the error Cannot read property 'getDescendentByEl' of undefined.

Router:

    scope "/sellers" do
      live "/new", SellerLive, :new
    end

LiveView:

defmodule MyApp.SellerLive do
  use MyAppWeb, :live

  def mount(_params, plug_session, socket) do
    IO.inspect "MOUNT"
    current_session = plug_session |> Map.get("my_app", %{}) |> Session.from_state()

    {:ok, assign(socket, :current_session, current_session)}
  end

  ...

Logs:

[info] GET /member/sellers/new
[debug] Processing with Phoenix.LiveView.Plug.new/2
  Parameters: %{}
  Pipelines: [:browser, :member]
"MOUNT"
[debug] QUERY OK ...
[info] Sent 200 in 33ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 246µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "LRIOCzcDKw0EOn5XY3tZMngXYSImGBA4ECmxcTOFAu6zV50y5aWjAzOg", "vsn" => "2.0.0"}
"MOUNT"
[debug] QUERY OK ...

@pedrogmucino
Copy link

I have exactly the same problem, apparently the file \apps\myapp web\priv\static\js\app.js is not updating causing this issue ¿Does anyone fixed the problem?

@josevalim
Copy link
Member

Remove assets/node_modules, then do a cd assets; npm install and either start your server in dev or re-run your deployment scripts.

@pedrogmucino
Copy link

@josevalim Done it and the problem persists

@ijunaid8989
Copy link

I have a form and phx_validate just get called once I load the page.

@tomtaylor
Copy link
Contributor

tomtaylor commented Jul 1, 2020

We see this intermittently on our CI environment on one specific end-to-end Cypress test. It's very difficult to reproduce locally.

It's possible it's being triggered by a browser click event occurring before the connected mount. Our end-to-end tests run very rapidly, much faster than a user would be able to click.

@josevalim I can privately share video captures of it occurring in Cypress, if useful? We will continue to see if we can reproduce locally.

@josevalim
Copy link
Member

Can you please try master? We have many fixes in there. Make sure you nuke node_modules after updating.

@tomtaylor
Copy link
Contributor

Thanks @josevalim, I'm afraid it's still occurring on master. I've blown away node_modules.

I can now reproduce locally, intermittently, inside Cypress. Here's the stack trace that Cypress produces from our compiled JS.

https://gist.github.com/tomtaylor/b3a262465b8eb258647023f1c8653df3

The source map links the first line (http://localhost:4003/js/admin.js:2108:37) back to this snippet.

{
key: "getViewByEl",
        value: function (e) {
          var t = e.getAttribute("data-phx-root-id");
          return this.getRootById(t).getDescendentByEl(e); <-- this line
        }
}

I can supply the code and the source map privately if useful.

It looks like this is related to the debounce function. We're not using debounce in this view, but there's a throttle on a form, which the link that causes this is inside.

@tomtaylor
Copy link
Contributor

I've updated the gist above to show the unobfuscated stack trace.

@tomtaylor
Copy link
Contributor

tomtaylor commented Jul 1, 2020

I can see in Cypress that the difference between the failing runs and the passing runs is that there's no data-phx-root-id attribute set on the <div data-phx-main=true> element when the button is pressed. I can reproduce this much more reliably by enabling the latency simulator with a 1s delay.

Does this attribute get set once the live socket is connected? If so, it might explain why pressing the button before the socket connection is made causes this issue.

@tomtaylor
Copy link
Contributor

I can now reliably work around this by explicitly waiting for the presence of a div with a phx-connected class before proceeding with the test.

@josevalim
Copy link
Member

Oh, I see. This makes sense. In the actual application, you can't use forms and friends until phx-connected is on, because we disable the cursor and everything. But your tests were not waiting for this, which causes said errors.

@tomtaylor
Copy link
Contributor

Yeah, I think Cypress does a bunch of trickery to trigger events as a user would, but I guess it's not respecting whatever mechanism LV uses to disable/enable these. Thanks for your help!

@Bumppoman
Copy link

I'm having this issue with a nested LiveView. My setup is a main page, "/foo", and several Bootstrap tabs, which can also be accessed by "/foo/bar", "/foo/baz", etc. Accessing "/foo" and clicking on the "bar" tab works fine, but directly accessing the URL "/foo/bar" causes this error.

Unfortunately, it kills JS execution on the page and makes the page unusable until refreshed. Still happening with 0.14.0.

@MeterSoft
Copy link

have some issue on 0.14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants