Ajax call in phoenix (respond_to :js in rails)

Hello everyone,

I have been spending some times Googling but didn’t find anything. In rails there is a way to do ajax call like this:

link_to some_path, remote: true

Then we can accept the js format controller side and render a view.js.erb that executes some JS directly on the page.

I wonder if this is doable in Phoenix. Should I write raw javascript ? Or is there something similar than in rails ?

Regards,
Robin

1 Like

Rails uses jquery-ujs to perform ajax calls and you can use it in your phoenix project by making little changes. i.e you can copy https://github.com/rails/jquery-ujs/blob/master/src/rails.js into your app.js and change csrf-token to _csrf_token as following

// Up-to-date Cross-Site Request Forgery token
csrfToken: function() {
   return $('meta[name=_csrf_token]').attr('content');
},

Now you can add "data-remote": "true" in form_for and your form will be submitted as xhr request. for example.

<%= form_for @changeset, @action, ["data-remote": "true"], fn f -> %>

You can add render(conn, "create.js.eex") in your controller just like rails.

i haven’t tested it yet but i hope it will work.

3 Likes

Thank you for the answer. I realise that I have used “respond_to js” in Rails without really knowing what was going on :slight_smile: ! Thank’s for the input I will avoid ajax call for now in my project, but good to know !

2 Likes

I’ve had pretty good luck using https://github.com/jalkoby/phoenix_ujs . It is a bit lighter weight than the full rails js.

2 Likes

Sort of in left field here, but depending on what you are trying to do … you may be able to use the UserSocket instead which in theory should be lighter weight / faster. The client would send it’s request over the websocket and it would respond with the appropriate data, which the client would then act on. Just a thought…

3 Likes

You can always control it server-side via Drab too.

2 Likes

Whoa Drab looks interesting. Looks like it works as @aseigo suggested me to do, using websockets and jquery. Very smart !!

1 Like

which in theory should be lighter weight / faster

not only in theory, I mostly use websockets (phoenix’ channels) everywhere where I used AJAX before, it’s simpler and faster this way. So if you have an option to switch now, that might be a good idea

1 Like

Any example on how you do this for say calling an action that modify a “model” and reload the part of the view that displays model data ?

1 Like

Honestly I’d love Drab to subsume Unpoly.js’s functionality into it, that is precisely what Unpoly.js does. :wink:

I’m using them together currently, but perfectly friction-less, but close.

1 Like

I call the functions from the service layer (that use models) that return :ok or error tuples, same as you do in controllers.

To avoid having too much dom manipulation I render templates to strings (same partials that are used on render) and pass them to frontend, so channels work much like controllers.

On the front end you might get away with jQuery (I did in the latest app), since there’s not much JS this way. For a proper SPA I’d go for Vue or React or something.

It’s not easy to deliver an example without over-simplifying or polluting it with non relevant parts, I’ll do my best though, hope it still paints a picture :slight_smile:

// some_socket.js 
// import base socket stuff and create a socket / channel vars
const getItems = (opts, onSuccess, onError) => {
  base.pushMessage(channel, "get-items", opts, onSuccess, onError);
};

export {someSocket as socket};
export {getItems as getItems};


// some_template_partial.html.eex
let someSocket = require('app/some_socket');
someSocket.socket.connect();

let onSuccess = function(response) {
  $("#partial-id").replaceWith(response.some_html_string);
  // render info notification
}
let onError = function(response) {
  // render error notification
}

$someElement.click(function(e){
    e.preventDefault();
    // get relevant data from dom
    someSocket.getItems(data, onSuccess, onError);
});


 // some_channel.ex
 def handle_in("get-items", payload, socket) do         
   case SomeService.get_items(payload) do
     {:ok, items} ->
       html = render_to_string(LayoutView, "partials/some.html", [socket: socket,  items: items])
       {:reply, {:ok, %{some_html_string: html}}, socket}
     {:error, reason} -> {:reply, {:error, %{reason: reason}}, socket}
   end
 end

Also check out Drab and Unpoly as suggested by @OvermindDL1 - they use similar idea but provide more abstractions.

What I find nice about this approach - it’s pretty clean and organized (despite all the love jQuery front ends get), very fast (often feels like working offline) and you get less rituals with all the actions, api routes etc that you get when working with AJAX.

4 Likes

Awesome thank you all for your answers I see clearly what are good practices about this now :slight_smile:

1 Like

Murtza I tried this but its not working