Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phoenix Framework: Send rendered template in JSON response

I have a template for the top navigation of my app in top_navigation.html which holds the "Sign in", "Sign up", and when logged in, the "Sign out" links.

<%= if logged_in?(@conn) do %>
  <li><%= link "Sign out", to: session_path(@conn, :delete), method: :delete %></li>
<% else %>
  <li><a href="#" class="js-register" data-toggle="modal" data-target=".js-register-modal">Sign up</a></li>
  <li><a href="#" class="js-login"    data-toggle="modal" data-target=".js-login-modal"   >Sign in</a></li>
<% end %>

I log the user in through AJAX, but once they're logged in, I would like to swap out the top navigation with the newly rendered template (showing the "Sign out" link), passed back to the client in the login response.

Is there a way to send the rendered template as part of the JSON response?

Something along the lines of:

defmodule MyApp.SessionController do
  use MyApp.Web, :controller

  def create(conn, %{"user" => user_params}) do
    case MyApp.Session.login(user_params, MyApp.Repo) do
      {:ok, user} ->
        conn
        |> put_session(:current_user, user.id)
        |> json %{ top_navigation: render("top_navigation.html") }
                                   # ^^^^ this doesn't work ^^^^
      :error ->
        conn
        |> put_status(404)
        |> json %{ message: "Unable to sign in." }
    end
  end

  def delete(conn, _) do
    conn
    |> delete_session(:current_user)
    |> put_flash(:info, "Signed out.")
    |> redirect(to: "/")
  end
end

And the Javascript:

$(".js-login").on("click", e => {
  e.preventDefault()
  $(".js-login-alert").hide()
})
$("#login").on("submit", e => {
  e.preventDefault()

  let form = $("#login")
  let data = { _csrf_token: $( 'input[name="_csrf_token"]' ).val(), 
               user:        { email:    form.find('input[name="email"]'   ).val(), 
                              password: form.find('input[name="password"]').val() } }

  $.ajax({
    type: "POST",
    url: "/login",
    data: data,
    success: e => {
      $(".js-top-navigation").html(e.responseJSON["top_navigation"])
      // ^^^^^^^^^^^^^^ This is where we swap it out ^^^^^^^^^^^^^^^
      $(".js-login-modal").modal("toggle")
    },
    error: e => {
      let alert = $(".js-login-alert")
      alert.text(e.responseJSON["message"])
      alert.show()
    }
  })
})

Thanks so much in advance!

like image 898
David Cole Avatar asked Sep 26 '22 12:09

David Cole


1 Answers

Yes indeed! Phoenix templates are just functions on their view module. So you can render a template by calling a function:

|> json(%{nav: Phoenix.View.render_to_string(MyView, "nav.html", conn: conn)})
like image 155
Chris McCord Avatar answered Sep 30 '22 08:09

Chris McCord