I'm having trouble working with the primereact ui library, specifically this (trimmed for space) templating example from https://primefaces.org/primereact/showcase/#/datatable/templating:
export class DataTableTemplatingDemo extends Component {
constructor(props) {
super(props);
this.state = {
products: []
};
this.ratingBodyTemplate = this.ratingBodyTemplate.bind(this);
this.statusBodyTemplate = this.statusBodyTemplate.bind(this);
}
ratingBodyTemplate(rowData) {
return <Rating value={rowData.rating} readOnly cancel={false} />;
}
statusBodyTemplate(rowData) {
return <span className={`product-badge status-${rowData.inventoryStatus.toLowerCase()}`}>{rowData.inventoryStatus}</span>;
}
render() {
return (
<div className="datatable-templating-demo">
<div className="card">
<DataTable value={this.state.products}>
<Column field="name" header="Name"></Column>
<Column field="rating" header="Reviews" body={this.ratingBodyTemplate}></Column>
<Column header="Status" body={this.statusBodyTemplate}></Column>
</DataTable>
</div>
</div>
);
}
}
As you can see, this feature allows you to write custom templates for elements in a data table by defining the functions within the lements, and then referring to them in the 'body' prop of the column you want to use the template. My stab at a roughly equivalent cljs is (again, I'm trimming way down to show the part I'm having trouble with, so I know there are other parts that might not be working here):
(defn junk-button [_] [:> Button {:label "test"}])
(defn table []
(let [del-button (r/as-element junk-button)]
[:> DataTable {:value table-data
:on-row-reorder reorder
:resizableColumns true}
[:> Column {:header "Delete"
:body [del-button]}]]))
When I actually load info into the table, I get an error saying "Uncaught TypeError: this.props.body is not a function".
Any ideas how to define a custom element in cljs here? Many thanks!!In the JSX example, you pass the function. In the CLJS code, you pass a vector - why? Just pass the function.
Apart from that, it still won't work because Column
expects that function to return a React element, but in your case it returns Hiccup. Wrap its result with reagent.core/as-element
.
Thanks for the response. I think I had it in the vector because when I pass it in directly as you suggest, I get a different error: Uncaught Error: Objects are not valid as a React child (found: object with keys {ns, name, fqn, _hash, cljs$lang$protocol_mask$partition0$, cljs$lang$protocol_mask$partition1$}). If you meant to render a collection of children, use an array instead.
That's exactly because you didn't use as-element
.
no, it happens when I use as-element
. Here is the code that throws the "Objects are not valid as React Child" error: (defn test []
(let [pdfs (-> @comp-state
:pdfs
vals)
table-data (->> @comp-state
:pdfs
vals
(sort-by :order)
(map (fn [m] (dissoc m :file :data-url)))
(map (fn [m] (assoc m :seq (nominal-order (:order m)))))
clj->js)
del-button (r/as-element junk-button)]
[:div {:style {:min-height 350}}
[:> DataTable {:ref (fn [e] (swap! comp-state assoc :dt e))
:value table-data
:on-row-reorder reorder
:resizableColumns true}
[:> Column {:header "Delete"
:body del-button}]]]))
Because you used it wrong. It accepts Hiccup, not a function. https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md#example-function-as-child-components
You have to use as-element
inside junk-button
. The function that you pass to Column
as :body
has to return a React element, and as-element
creates a React element from a Hiccup vector.
Bingo! Thanks so much!!!!!!
I have trouble understanding the r/track
...
as far as I understand it should work similarly to make-reaction
, as the documentation itself suggests
but...
(defonce app-state (r/atom {:people {1 {:name "John Smith"} 2 {:name "Maggie Johnson"}}})) (defn people [] (print :fire 'people " ") (:people @app-state)) (defn person [id] (print :fire 'person " ") (-> @(r/track people) (get id))) (with-out-str (person 1)) ;; => ":fire person :fire people " (with-out-str (person 1)) ;; => ":fire person :fire people " (with-out-str (person 1)) ;; => ":fire person :fire people "
even though the atom hasn't changed, every time it fires all the functions along the way and it doesn't look like anything is cached
I think the issue here is that person
each time returns new instance of defer-able.
it looks like I don't understand reaction either, and I was almost sure I did
(def a (r/atom 10))
(def r1 (ra/reaction (let [n (rand-int @a)] n)))
@r1
;; => 6
@r1
;; => 3
You can't rely on any reactive behavior outside of a reactive context. Views are such a context, when they're rendered by Reagent. Such as other reactions that are themselves used in a reactive context. But REPL is not a reactive context.
I'm pretty sure that's how I tested it in the past
I just tested it in a reactive context. Works just fine.
(ns app.core
(:require [reagent.core :as reagent]
[reagent.ratom :as rea]
[reagent.dom]
[clojure.browser.dom]))
(defn view [a b]
(reagent/with-let [x (rea/reaction (+ @a (rand-int 10000)))]
[:div @x ";" @b]))
(defn app []
(reagent/with-let [a (reagent/atom 0)
b (reagent/atom 0)]
[:div
[:button {:on-click #(swap! a inc)} "Inc A"]
[:button {:on-click #(swap! b inc)} "Inc B"]
[view a b]]))
(defn ^:export main []
(reagent.dom/render [app] (clojure.browser.dom/get-element :app)))
So if you press Inc B
, the part before ;
won't change, as expected.
Thx!