0

I defined a helper function in the view:

def usernames(id) do
  post = Repo.get(Post, id) |> Repo.preload(comments: :user)
  usernames = for comment <- post.comments do
     comment.user.username
  end
end

Then in the javascript file I do:

$('#comment').doThis({
   data: "<%= escape_javascript( MyApp.ApplicationHelpers.usernames(@post.id) ) %>"
});

The above does not work. Of course I need to be able to pass in the @post.id, but even by hardcoding a post id like MyApp.ApplicationHelpers.usernames(5), doesn't work.

Basically I am trying to do the Rails equivalent of:

@usernames = User.pluck(:username)

Then in the js.erb would have been:

data = <% @usernames %>
$('#comment').doThis({'data': data});

If someone can help out in how to achieve this...

Update: (to provide additional info) I put the javascript code in posts.js as following this stackoverflow answer. Other javascript code in that file executes as needed.

codingbear
  • 296
  • 5
  • 18
  • Could you specify where is your javascript file and the extension of it? Because I think you are mixing .eex with .js which is not possible afaik. – emancu May 24 '17 at 00:11
  • Hey @emancu I added information in the update to the question, as you asked. – codingbear May 24 '17 at 00:18
  • Possible duplicate of [How to execute elixir code in a js file?](https://stackoverflow.com/questions/41136258/how-to-execute-elixir-code-in-a-js-file) – Patrick Oscity May 24 '17 at 05:27
  • @PatrickOscity Thanks so much for stopping by at the question. Your answer on the linked question (basically hard coding it in as a data attribute) was very helpful. It worked precisely as needed in a scenario whereby I needed the post url and post title in the javascript file. But for this one, though your solution would have worked, it was not feasible to hard code all the users, especially since if the user list grows. – codingbear May 24 '17 at 05:42
  • 1
    @codingbear yeah you're right, hard coding is not an option here but a global `window` object, data attributes or separate JSON endpoint would be feasible. If you're concerned with the size of the data attribute - [in HTML 5 there is no size limit on attributes](https://stackoverflow.com/questions/1496096/is-there-a-limit-to-the-length-of-html-attributes) so you should be fine. Just wanted to let you know that embedding Elixir code in static assets is not really an option with Phoenix and which solutions exist to handle such situations. – Patrick Oscity May 24 '17 at 07:31
  • @PatrickOscity wow thanks for that comment, thats amazing to learn that there's no size limit on the the data attributes in HTML5. I give you one up vote for mentioning and linking to that information. So in the next similar scenario I am gonna try doing JSON which I think I will need to start generating as the app requires. Finally, thank you for mentioning the options available with Phoenix. – codingbear May 24 '17 at 17:01

1 Answers1

2

Well @codibgbear the thing is, phoenix will take that as a regular js file so it won't compile the code, which means that your EEX syntax won't work.

What you can do is to define a partial next to your template called _scripts.html.eex and add the following code

<script>
  $('#comment').doThis({
    data: "<%= escape_javascript(usernames(@post.id)) %>"
  });
</script>

Then on your template, make sure you are rendering this partial.

<%= render "_scripts.html", post: @post %>

Actually, you should check your templates/layout/app.html.eex file and see if there is a line already to render this file magically ;)

The line should look like:

<%= render_existing view_module(@conn), "_scripts.html", assigns %>

And since you defined the usernames/1 function in the view, you don't need to use the entire module name, so I think this should work.

Good luck!

emancu
  • 145
  • 1
  • 9
  • Hey @emancu, Thanks for giving an answer. I am going to try it out. – codingbear May 24 '17 at 01:20
  • Hey @emancu, I added your suggested code, and as I was doing it, your logic made sense. I added this line as you suggested `<%= render_existing view_module(@conn), "_scripts.html", conn: @conn %>` to `templates/layout/app.html.eex`, but I get the error that `assign @post not available in eex template`. How do I pass on the @post to the available assigns. – codingbear May 24 '17 at 01:50
  • Well. You are actually sending the @conn so you should be able to get the post from there. Something like '@conn.assigns.post' if you assigned it in the controller. – emancu May 24 '17 at 04:50
  • 1
    it ended up working out like this: `<%= render_existing view_module(@conn), "_scripts.html", assigns %>`. if you amend it to that I could mark it as correct. I am gonna vote it up at the moment. – codingbear May 24 '17 at 05:04
  • Thanks so much @emancu for taking the time and writing up the answer as well as following up on the comments. – codingbear May 24 '17 at 16:44