Search code examples
clojuremustache

How can I select the correct option in select box in a nested loop in mustache?


I'm having a nested loop that generates <select> tags, and I want to set them to a certain value. I thought I could create a lambda {{selected}} that would take the context and check if it's the correct value for this selectbox. Except I'd also need the id from the {{team}} array.

For a single select, you could include a selected boolean in champions, but this gets messy pretty quick quick select boxes.

Would there be a way to get the needed info, or another way to generated the selected attribute? I'm using Clojure's Stencil with data the looks like this:

  {:params {"winner0" 16, "winner1" 4, ...}
   :champions [{:name "Soraka", :id 16}, ...]
   :team (range 5)
   :selected (fn [] if params[idx] == champion-id: return selected)}

HTML excerpt:

  <form method="get" action="">
          {{#team}}
                  <select name="winner{{.}}">
                      <option value="">None</option>
                      {{#champions}}
                      <option {{selected}} value="{{id}}">{{name}}</option>
                      {{/champions}}
                  </select>
          {{/team}}
      <input type="submit" value="Recommend my pick!" />
  </form>

Solution

  • Mustache templates are supposed to be "dumb". Even though there exist a lambda construct, it has very limited capabilities. Your best bet is to transform the data so it's already "massaged" for the template. Clojure shines here: it has very powerful data transformation primitives.

    Here's an example (reduced for brevity) based on yours:

    (require '[stencil.core :as stencil])
    
    (def form "{{#teams}}
                  <select name='winner{{id}}'>
                    <option value=''>None</option>
                    {{#champions}}
                    <option {{#selected}}selected='selected' {{/selected}}value='{{id}}'>{{name}}</option>
                    {{/champions}}
                  </select>
               {{/teams}}")
    
    
    (let [data {:params {"winner0" 16, "winner1" 4}
                :champions [{:name "Soraka", :id 16}
                            {:name "champ4" :id 4}
                            {:name "champ5" :id 5}]}]
      (->> (range 2)
           (map (fn [team-id]
                  (let [winner (get (:params data) (str "winner" team-id))]
                    {:id team-id
                     :champions (map #(assoc % :selected (= (:id %) winner))
                                     (:champions data))})))
           (hash-map :teams)
           (stencil/render-string form))
    

    Result:

    <select name='winner0'>                                                                                                                                                                                                                                        
      <option value=''>None</option>                                                                                                                                                                                                                               
      <option selected='selected' value='16'>Soraka</option>                                                                                                                                                                                                       
      <option value='4'>champ4</option>                                                                                                                                                                                                                           
      <option value='5'>champ5</option>                                                                                                                                                                                                                           
    </select>                                                                                                                                                                                                                                                      
    <select name='winner1'>                                                                                                                                                                                                                                        
      <option value=''>None</option>                                                                                                                                                                                                                               
      <option value='16'>Soraka</option>                                                                                                                                                                                                                          
      <option selected='selected' value='4'>champ4</option>                                                                                                                                                                                                        
      <option value='5'>champ5</option>                                                                                                                                                                                                                           
    </select> 
    

    Even though that seems to do what you need, if you have the chance to choose I strongly recommend using something like hiccup. It's a DSL to represent html using clojure data structures, so it's very expressive and powerful.