Search code examples
validationclojurenoir

Build Noir validations rules with map


I've got dynamically generated forms, so I was trying to validate them this way:

(defn valid? [media-id data] ;media-id it's just a number, data is the form input
  (let [fields (common/get-fields-to-show media-id)] ; list of strings (the field names)
    (map (fn [f]
           (vali/rule (vali/has-value? ((keyword f) data))
                      [(keyword f) "Write something!!"]))
         fields))
    (not (apply vali/errors? (map keyword fields))))

but it won't work. There's no exception or message at all, valid? is evaluated as true so the flow continues as if there were no errors although all fields are empty. I even tried (vali/has-value? nil) to force the error but nothing changes.

Experimenting, I removed the map, took two specific fields, build their rules "by hand" this way:

(defn valid? [media-id data]
  (let [fields (common/get-fields-to-show media-id)
        f1 (first fields)
        f2 (second fields)]
    (vali/rule (vali/has-value? ((keyword f1) data))
               [(keyword f1) "Testing"])
    (vali/rule (vali/has-value? ((keyword f2) data))
               [(keyword f2) "Testing"])
    (not (apply vali/errors? (map keyword fields))))

And it works perfectly for those lucky fields.

I suspect it has something to do with the way noir.validation saves the errors (a dynamic declared thing), but I'm not sure.


Solution

  • Don't use map for sequence of operations. map is for transforming a sequence to something else. What you need to use is doseq

    Instead of:

    (map (fn [f]
               (vali/rule (vali/has-value? ((keyword f) data))
                          [(keyword f) "Write something!!"]))
             fields))
    

    Use this:

    (doseq [f fields]
        (vali/rule (vali/has-value? ((keyword f) data))
                              [(keyword f) "Write something!!"]))