0

So I have the ability to list all users in from a database in a view, but I would like to list them alphabetically and by tab. For example, I'll have a tab for "All", "A", "B", "C", etc. What I would like to happen is that when the user clicks the "A" tab, it displays only the users with last name starting with "A".

My idea for doing this is such:

<ul class="tabs">
  <li><a href="#A">A</a></li>
  <li><a href="#B">B</a></li>
  <li><a href="#C">C</a></li>
  ...
</ul>
<div class="tabcontents">
  <div id="All">
    <nav>
      <ul>
        <g:each var="user" in="${users}" ID="">
          <li><g:link controller="user" action="profile" id="${user.employeeNumber}">${user.lastName}, ${user.firstName}</g:link></li>
        </g:each>
      </ul>
    </nav>
  </div>
  <div id="A">
    <nav>
      <ul>
        <g:each var="user" in="${users}" ID="A">
          <li><g:link controller="user" action="profile" id="${user.employeeNumber}">${user.lastName}, ${user.firstName}</g:link></li>
        </g:each>
      </ul>
    </nav>
  </div>
  ...
</div>

with the back end UserController containing something like

class UserController {

  def index() {
    def users = UserFeed.createCriteria().list {
      eq('lastName[0]', '${ID}')
    }
    render(view: 'index', model: [users: users])
  }

  def search() {
  }

  def results(String lastName) {
    def users = UserFeed.where { 
      lastName =~ "%${lastName}%" 
    }.list()
    return [ users: users,
         term: params.lastName,
         totalUsers: UserFeed.count() ]
  }

    def profile(String id) {
    def user = UserFeed.findByEmployeeNumber(id)
    render(view: 'profile', model: [user: user])
    }
}

So the idea being, when the user clicks a tab, the letter of the tab is sent to the controller where the first letter of the last name in the database is compared and added to the list if it matches. Is this possible? Is there a slick way to do this?

Thanks in advance!

Edit: I can brute force this, I just would like to avoid having 26 groups of users for each letter of the alphabet.

Edit 2: I've tried something like this, but to no avail

<div id="A">
  <nav>
    <ul>
      <g:each var="user" in="${users}">
        <g:if test="${user.lastName.charAt(0)} == 'A'">
          <li style="padding-left:5px;"><g:link controller="user" action="profile" id="${user.employeeNumber}">${user.lastName}, ${user.firstName}</g:link></li>
        </g:if>
      </g:each>
    </ul>
  </nav>
</div>

I've verified that user.lastName.charAt(0) prints out the correct letter, but for whatever reason, the g:if statement doesn't limit the list to just A's. Any ideas why?

mjswartz
  • 715
  • 1
  • 6
  • 19
  • you could look at html-5 data-attributes to store information then trigger via jquery to post when row is clicked that then returns whatever you pick out from data-attribute you have defined to search for http://html5doctor.com/html5-custom-data-attributes/ – V H Jul 31 '15 at 19:59

1 Answers1

1

I think a good place to start is with a model that's easy to render into tabs. For instance, the controller method can return a model which contains a Map containing letters for the keys and the users which go under each letter for values.

Starting with a list of users and the selected letter, here's an example of how to create such a Map in Groovy:

def selectedLetter = 'A'
def tabs = users
     .groupBy { it.lastName[0].toUpperCase() }
     .collectEntries { k, v -> [k, v.sort() {a, b -> a.lastName <=> b.lastName}] }   
     .collectEntries { k, v -> k == selectedLetter ? [k, v] : [k, null] }

This returns a sparse-map; only users for the selected letter are included by setting all others to null. Next, Return a model containing the tabs:

render(view: 'index', model: [tabs: tabs])

With such a model in place the tabs can be rendered dynamically as follows:

<ul class="tabs">
    <g:each var="letter" in="${tabs.keySet().sort()}">
        <li class="${tabs.letter != null ? 'active' : ''">
            <a href="#${letter}">${letter}</a>
        </li>
    </g:each>
</ul>

And the tab contents can be rendered like this:

<div class="tabcontents">
    <g:each var="letter" in="${tabs.keySet().sort()}">
        <div id="${letter}">
            <nav>
              <ul>
                <g:each var="user" in="${tabs.letter}">
                  <li><g:link controller="user" action="profile" id="${user.employeeNumber}">${user.lastName}, ${user.firstName}</g:link></li>
                </g:each>
              </ul>
            </nav>
        </div>        
    </g:each>  
</div>
Emmanuel Rosa
  • 9,697
  • 2
  • 14
  • 20
  • Emmanuel, this is pretty cool, but my issue is that the number of users under each tab may grow as users are added. Do you have an idea for how to handle that? – mjswartz Aug 03 '15 at 13:02
  • In my example i showed a fixed set of users for brevity. You'd have to tweak the code a bit to support a list of user objects rather than a list of strings but the logic should work. – Emmanuel Rosa Aug 03 '15 at 13:10
  • Emmanuel, do you have any insight into why my Edit 2 doesn't work? I've also tried it with `.equals('A')` insead of `==` – mjswartz Aug 03 '15 at 13:26
  • Yes. The test expression is wrong because you put the closing curly bracket too early. It should be like this ` "${user.lastName.charAt(0) == 'A'}"` – Emmanuel Rosa Aug 03 '15 at 13:52
  • I updated my example to use User objects. And my last comment was not formatted correctly. It should be: `"${user.lastName.charAt(0) == 'A'}"` – Emmanuel Rosa Aug 03 '15 at 15:18