Can you help me understand and modify this Coherence View Helper?

Hi,

I’ve just got Coherence integrated and working, and now am trying to customize the look of it, in particular the following code that displays links for joining and logging in. They work fine, but I want to display buttons instead. My problem is that I’m trying to work out how to put the href (link) inside the button.

Here’s what appears to be the relevant bit of code from coherence_view_helpers.ex

def coherence_links(conn, :layout, opts) do
    list_tag      = Keyword.get opts, :list_tag, :li
    signout_class = Keyword.get opts, :signout_class, "navbar-form"
    signin        = Keyword.get opts, :signin, @signin_link
    signout       = Keyword.get opts, :signout, @signout_link
    register      = Keyword.get opts, :register, @register_link

    if Coherence.logged_in?(conn) do
      current_user = Coherence.current_user(conn)
      [
        content_tag(list_tag, current_user.name),
        content_tag(list_tag,
          link(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))
      ]
    else
      signin_link = content_tag(list_tag, link(signin, to: coherence_path(@helpers, :session_path, conn, :new)))
      if Config.has_option(:registerable) && register do
        [content_tag(list_tag, link(register, to: coherence_path(@helpers, :registration_path, conn, :new))), signin_link]
      else
        signin_link
      end
    end
  end

Here’s my noob attempt at changing it to a button, which is obviously wrong as it outputs the link wrapped within <button></button> tags and so doesn’t work.

def coherence_links(conn, :layout, opts) do
    button_tag    = Keyword.get opts, :button_tag, :button
    signout_class = Keyword.get opts, :signout_class, "navbar-form"
    signin        = Keyword.get opts, :signin, @signin_link
    signout       = Keyword.get opts, :signout, @signout_link
    register      = Keyword.get opts, :register, @register_link

    if Coherence.logged_in?(conn) do
      current_user = Coherence.current_user(conn)
      [
        content_tag(button_tag, profile_link(current_user, conn)),
        content_tag(button_tag,
          link(signout, to: coherence_path(@helpers, :session_path, conn, :delete), method: :delete, class: signout_class))
      ]
    else
      signin_link = content_tag(button_tag, link(signin, to: coherence_path(@helpers, :session_path, conn, :new)))
      if Config.has_option(:registerable) && register do
        [content_tag(button_tag, link(register, to: coherence_path(@helpers, :registration_path, conn, :new))), signin_link]
      else
        signin_link
      end
    end
  end

Here’s the full code from the Coherence demo repo:
https://github.com/smpallen99/coherence_demo/blob/master/web/views/coherence/coherence_view_helpers.ex

It’s a super noob question, but there’s a lot of code for me to work through, and although I can pick out lists [] and things, I’m really failing at this.

Have you tried using button/2 instead of link/2?

# Before
link(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))

# After
button(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))
1 Like

Thanks,

I’ve tried using this in the following context:

if Config.has_option(:registerable) && register do
        [content_tag(button_tag, button(register, to: coherence_path(@helpers, :registration_path, conn, :new))), signin_link]
      else

as part of:

def coherence_links(conn, :layout, opts) do
    button_tag    = Keyword.get opts, :button_tag, :button
    signout_class = Keyword.get opts, :signout_class, "navbar-form"
    signin        = Keyword.get opts, :signin, @signin_link
    signout       = Keyword.get opts, :signout, @signout_link
    register      = Keyword.get opts, :register, @register_link

    if Coherence.logged_in?(conn) do
      current_user = Coherence.current_user(conn)
      [
        content_tag(button_tag, profile_link(current_user, conn)),
        content_tag(button_tag,
          link(signout, to: coherence_path(@helpers, :session_path, conn, :delete), method: :delete, class: signout_class))
      ]
    else
      signin_link = content_tag(button_tag, button(signin, to: coherence_path(@helpers, :session_path, conn, :new)))
      if Config.has_option(:registerable) && register do
        [content_tag(button_tag, button(register, to: coherence_path(@helpers, :registration_path, conn, :new))), signin_link]
      else
        signin_link
      end
    end
  end

and it gives 4 buttons -

<button><form action="/registrations/new" class="button" method="post"><input name="_csrf_token" type="hidden" value="f2sFQXkWBiMvWR8BNXkTASANEDpsNgAAK/m34L1Ia6JHYMzbwBepCA=="><button type="submit">Need An Account?</button></form></button><button><form action="/sessions/new" class="button" method="post"><input name="_csrf_token" type="hidden" value="f2sFQXkWBiMvWR8BNXkTASANEDpsNgAAK/m34L1Ia6JHYMzbwBepCA=="><button type="submit">Sign In</button></form></button>

Obviously the above is a clusterf–k of a mess, but at least working buttons are generated.

How can I remove the broken/munged buttons? I’ve tried removing the button_tag and associated code: button_tag = Keyword.get opts, :button_tag, :button but I’m not sure how to rewrite button/2 and the code it’s wrapped in.

I believe you can just take what you originally had, the code that uses link and list_tag, and replace both occurrences of link/2 with button/2.

1 Like

Thanks, I’m part way there now - not sure if I’ve implemented what you are saying right?

def coherence_links(conn, :layout, opts) do
button_tag    = Keyword.get opts, :button
button_class = Keyword.get opts,  :button_class, "btn btn-outline-primary"
signout_class = Keyword.get opts, :signout_class, "navbar-form"
signin        = Keyword.get opts, :signin, @signin_link
signout       = Keyword.get opts, :signout, @signout_link
register      = Keyword.get opts, :register, @register_link

if Coherence.logged_in?(conn) do
  current_user = Coherence.current_user(conn)
  [
    content_tag(button_tag, profile_link(current_user, conn)),
    content_tag(button_tag,
      link(signout, to: coherence_path(@helpers, :session_path, conn, :delete), method: :delete, class: signout_class))
  ]
else
  signin_link = content_tag(button_tag, button(signin, to: coherence_path(@helpers, :session_path, conn, :new), class: button_class))
  if Config.has_option(:registerable) && register do
    [content_tag(button_tag, button(register, to: coherence_path(@helpers, :registration_path, conn, :new), class: button_class)), signin_link]
  else
    signin_link
  end
end

end

I’ve added the button_class line in the struct(? Sorry I’m a noob at elixir) and the :button_class atom, and using them in the button/2 code below it.

It’s styling both buttons as per this screenshot from the rightside of the nav bar, but has extra ‘<>’:

Here’s the inspected html:

<>Need An Account?<>Sign In

I’d try this:

def coherence_links(conn, :layout, opts) do
  list_tag      = Keyword.get opts, :list_tag, :li
  signout_class = Keyword.get opts, :signout_class, "navbar-form"
  signin        = Keyword.get opts, :signin, @signin_link
  signout       = Keyword.get opts, :signout, @signout_link
  register      = Keyword.get opts, :register, @register_link

  if Coherence.logged_in?(conn) do
    current_user = Coherence.current_user(conn)
    [
      content_tag(list_tag, current_user.name),
      content_tag(list_tag,
        button(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))
    ]
  else
    signin_link = content_tag(list_tag, link(signin, to: coherence_path(@helpers, :session_path, conn, :new)))
    if Config.has_option(:registerable) && register do
      [content_tag(list_tag, button(register, to: coherence_path(@helpers, :registration_path, conn, :new))), signin_link]
    else
      signin_link
    end
  end
end
1 Like

Thanks,

I hope I’m not working against what you are suggesting by adding a button_class struct element and atom?

def coherence_links(conn, :layout, opts) do
    list_tag      = Keyword.get opts, :list_tag, :li
    button_class  = Keyword.get opts, :button_class, "btn btn-outline-primary"
    signout_class = Keyword.get opts, :signout_class, "navbar-form"
    signin        = Keyword.get opts, :signin, @signin_link
    signout       = Keyword.get opts, :signout, @signout_link
    register      = Keyword.get opts, :register, @register_link

    if Coherence.logged_in?(conn) do
      current_user = Coherence.current_user(conn)
      [
        content_tag(list_tag, current_user.name),
        content_tag(list_tag,
          button(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))
      ]
    else
      signin_link = content_tag(list_tag, link(signin, to: coherence_path(@helpers, :session_path, conn, :new), class: button_class))
      if Config.has_option(:registerable) && register do
        [content_tag(list_tag, button(register, to: coherence_path(@helpers, :registration_path, conn, :new), class: button_class)), signin_link]
      else
        signin_link
      end
    end
  end

Here’s what this is outputting - one button as part of a form and the other as part of a list:

<div class="nav navbar-nav form-inline pull-sm-right">
        <li><form action="/registrations/new" class="button" method="post"><input name="_csrf_token" type="hidden" value="HzEHEX0+Ai4vAjQ9FGMnMA0sBXoeNgAA+uoc0d5DamatxWNSZcp01A=="><button class="btn btn-outline-primary" type="submit">Need An Account?</button></form></li><li><a class="btn btn-outline-primary" href="/sessions/new">Sign In</a></li>
    </div>

The Sign In button works, but the Need an Account button does although it outputs the right URL/URI.

Ultimately, I want the buttons horizontally aligned - side by side and spaced apart.

Fixed, mostly now -

I removed button/2 and re-used link/2 and used button_class for both links.

def coherence_links(conn, :layout, opts) do
    list_tag      = Keyword.get opts, :list_tag, :li
    button_class  = Keyword.get opts, :button_class, "btn btn-outline-primary"
    signout_class = Keyword.get opts, :signout_class, "navbar-form"
    signin        = Keyword.get opts, :signin, @signin_link
    signout       = Keyword.get opts, :signout, @signout_link
    register      = Keyword.get opts, :register, @register_link

    if Coherence.logged_in?(conn) do
      current_user = Coherence.current_user(conn)
      [
        content_tag(list_tag, current_user.name),
        content_tag(list_tag,
          button(signout, to: coherence_path(@helpers, :session_path, conn, :delete, current_user), method: :delete, class: signout_class))
      ]
    else
      signin_link = content_tag(list_tag, link(signin, to: coherence_path(@helpers, :session_path, conn, :new), class: button_class))
      if Config.has_option(:registerable) && register do
        [content_tag(list_tag, link(register, to: coherence_path(@helpers, :registration_path, conn, :new), class: button_class)), signin_link]
      else
        signin_link
      end
    end
  end

That gives:

<div class="nav navbar-nav form-inline pull-sm-right">
        <li><a class="btn btn-outline-primary" href="/registrations/new">Need An Account?</a></li><li><a class="btn btn-outline-primary" href="/sessions/new">Sign In</a></li>
    </div>

If I’ve done it right, now I just need to horizontally align the links in the list.

Am I doing this right?

Something isn’t right. That li's parent should be a ul, not a div. Anyway, this worked for me:

https://github.com/dsissitka/coherence_demo/commit/59a88f335d5147df49dbf6598e59bd14288aeb67

Imgur

Is that what you’re looking for?

2 Likes

Thanks, I haven’t got your code to work yet, but am trying.

I found that I had modified the <ol> in app.html.eex while trying to get things working last night to be a <div>. I’ve now returned it into a <ul>.

The output is:

<ul class="nav navbar-nav float-sm-right">
    <li><a class="btn btn-outline-primary" href="/registrations/new">Need An Account?</a></li>
    <li><a class="btn btn-outline-primary" href="/sessions/new">Sign In</a></li>
</ul>

But it’s not presenting the list inline, so the buttons are still stacked on top of each other.

Perhaps I’m misreading Bootstrap’s docs?

1 Like

Are you using Bootstrap 4? If so, it looks like you need to add class="nav-item" to your lis and class="nav-link" to your as. From https://v4-alpha.getbootstrap.com/components/navbar/#nav:

<nav class="navbar navbar-light bg-faded">
  <ul class="nav navbar-nav">
    <li class="nav-item active">
      <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">Features</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">Pricing</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">About</a>
    </li>
  </ul>
</nav>

What does your coherence_links/3 look like now?

2 Likes

Actually, I don’t know if that will work. It looks like the only supported way to use buttons in a navbar is in a form:

If they have to be buttons it might be easier to drop the navbar and use a grid.

2 Likes

Thanks, I just tried using <li class="nav-item"> in a browser inspector and it works!

And now I’ve integrated into my app and it works.

Thank you so much!!

1 Like