diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java --- a/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java Thu Dec 18 10:44:48 2014 +0100 @@ -71,7 +71,7 @@ try { GC m = Models.bind(new GC(), ctx); m.getAll().add(new Fullname("Jarda", "Tulach")); - m.applyBindings(); + Models.applyBindings(m); int cnt = Utils.countChildren(GCKnockoutTest.class, "ul"); assert cnt == 1 : "One child, but was " + cnt; diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/JSONTest.java --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java Thu Dec 18 10:44:48 2014 +0100 @@ -57,7 +57,7 @@ * * @author Jaroslav Tulach */ -@Model(className = "JSONik", properties = { +@Model(className = "JSONik", targetId = "", properties = { @Property(name = "fetched", type = Person.class), @Property(name = "fetchedCount", type = int.class), @Property(name = "fetchedResponse", type = String.class), diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java Thu Dec 18 10:44:48 2014 +0100 @@ -58,7 +58,7 @@ * * @author Jaroslav Tulach */ -@Model(className="KnockoutModel", properties={ +@Model(className="KnockoutModel", targetId = "", properties={ @Property(name="name", type=String.class), @Property(name="results", type=String.class, array = true), @Property(name="numbers", type=int.class, array = true), diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/MinesTest.java --- a/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java Thu Dec 18 10:44:48 2014 +0100 @@ -57,7 +57,7 @@ /** Tests model of a mine field and its behavior in the browser. */ -@Model(className = "Mines", properties = { +@Model(className = "Mines", targetId = "", properties = { @Property(name = "state", type = MinesTest.GameState.class), @Property(name = "rows", type = Row.class, array = true), }) diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/PairModel.java --- a/json-tck/src/main/java/net/java/html/json/tests/PairModel.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java Thu Dec 18 10:44:48 2014 +0100 @@ -54,7 +54,7 @@ * * @author Jaroslav Tulach */ -@Model(className = "Pair", properties = { +@Model(className = "Pair", targetId = "", properties = { @Property(name = "firstName", type = String.class), @Property(name = "lastName", type = String.class), @Property(name = "next", type = Pair.class) diff -r d06b9f7976d1 json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java --- a/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Thu Dec 18 10:44:48 2014 +0100 @@ -53,7 +53,7 @@ * * @author Jaroslav Tulach */ -@Model(className = "WebSocketik", properties = { +@Model(className = "WebSocketik", targetId="", properties = { @Property(name = "fetched", type = Person.class), @Property(name = "fetchedCount", type = int.class), @Property(name = "open", type = int.class), diff -r d06b9f7976d1 json/src/main/java/net/java/html/json/Model.java --- a/json/src/main/java/net/java/html/json/Model.java Thu Dec 18 09:22:47 2014 +0100 +++ b/json/src/main/java/net/java/html/json/Model.java Thu Dec 18 10:44:48 2014 +0100 @@ -48,6 +48,7 @@ import java.lang.annotation.Target; import java.net.URL; import java.util.List; +import org.netbeans.html.json.spi.Technology; /** Defines a model class that represents a single * JSON-like object @@ -122,7 +123,7 @@ *
* In case you are using Knockout technology
* for Java then you can associate such model object with surrounding HTML page by
- * calling: p.applyBindings();
. The page can then use regular
+ * calling: p.applyBindings();
(in case you specify {@link #tar. The page can then use regular
* Knockout bindings to reference your
* model and create dynamic connection between your model in Java and
* live DOM structure in the browser:
@@ -202,4 +203,22 @@
* @return array of property definitions
*/
Property[] properties();
+
+ /** The id of an element to bind this model too. If this
+ * property is specified an applyBindings
method
+ * in the model class is going to be generated which later calls
+ * {@link Models#applyBindings(java.lang.Object, java.lang.String)}
+ * with appropriate targetId
. If the targetId
+ * is specified as empty string, null
value is passed
+ * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method.
+ * If the targetId
is not specified at all, no public
+ * applyBindings
method is generated at all (a change compared
+ * to previous versions of this API).
+ *
+ * @return an empty string (means apply globally), or ID of a (usually DOM)
+ * element to apply this model after calling its generated
+ * applyBindings()
method to
+ * @since 1.1
+ */
+ String targetId() default "";
}
diff -r d06b9f7976d1 json/src/main/java/net/java/html/json/Models.java
--- a/json/src/main/java/net/java/html/json/Models.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/net/java/html/json/Models.java Thu Dec 18 10:44:48 2014 +0100
@@ -164,6 +164,25 @@
* @since 0.7
*/
public static void applyBindings(Object model) {
- JSON.applyBindings(model);
+ JSON.applyBindings(model, null);
+ }
+
+
+ /** Apply bindings of a model class. Each model class has an
+ * apply bindings method that "activates" it. In ko4j mode,
+ * it binds the model values to the currently active page. One
+ * can call the generated method directly, or use this static
+ * helper method to perform the activation.
+ *
+ * @param model instance of a {@link Model class}
+ * @param targetId the id of the element to apply the binding to
+ * @throws IllegalArgumentException if the model
is not
+ * instance of a class generated by {@link Model model annotation}
+ * processor.
+ *
+ * @since 1.1
+ */
+ public static void applyBindings(Object model, String targetId) {
+ JSON.applyBindings(model, targetId);
}
}
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/impl/Bindings.java
--- a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java Thu Dec 18 10:44:48 2014 +0100
@@ -42,6 +42,8 @@
*/
package org.netbeans.html.json.impl;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import net.java.html.BrwsrCtx;
import org.netbeans.html.json.spi.FunctionBinding;
import org.netbeans.html.json.spi.PropertyBinding;
@@ -53,6 +55,8 @@
* @author Jaroslav Tulach
*/
public final class Bindings {
+ private static final Logger LOG = Logger.getLogger(Bindings.class.getName());
+
private Data data;
private final Technology bp;
@@ -105,7 +109,18 @@
}
}
- public void applyBindings() {
+ public void applyBindings(String id) {
+ if (bp instanceof Technology.ApplyId) {
+ Technology.ApplyId ai = (Technology.ApplyId) bp;
+ ai.applyBindings(id, data);
+ return;
+ }
+ if (id != null) {
+ LOG.log(Level.WARNING,
+ "Technology {0} does not implement ApplyId extension. Can't apply to {1}. Applying globally.",
+ new Object[]{bp, id}
+ );
+ }
bp.applyBindings(data);
}
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/impl/JSON.java
--- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java Thu Dec 18 10:44:48 2014 +0100
@@ -296,12 +296,12 @@
return find(object, null);
}
- public static void applyBindings(Object object) {
+ public static void applyBindings(Object object, String id) {
final Proto proto = findProto(object);
if (proto == null) {
throw new IllegalArgumentException("Not a model: " + object.getClass());
}
- proto.applyBindings();
+ proto.applyBindings(id);
}
public static void loadJSON(
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/impl/JSONList.java
--- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Thu Dec 18 10:44:48 2014 +0100
@@ -179,7 +179,7 @@
public void run() {
Bindings m = PropertyBindingAccessor.getBindings(proto, false);
if (m != null) {
- m.valueHasMutated(name, null, null);
+ m.valueHasMutated(name, null, JSONList.this);
for (String dependant : deps) {
m.valueHasMutated(dependant, null, null);
}
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
--- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Thu Dec 18 10:44:48 2014 +0100
@@ -499,17 +499,31 @@
w.append(" }\n");
writeToString(props, w);
writeClone(className, props, w);
- w.write(" /** Activates this model instance in the current {@link \n"
- + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
- + "In case of using Knockout technology, this means to \n"
- + "bind JSON like data in this model instance with Knockout tags in \n"
- + "the surrounding HTML page.\n"
- + "*/\n"
- );
- w.write(" public " + className + " applyBindings() {\n");
- w.write(" proto.applyBindings();\n");
- w.write(" return this;\n");
- w.write(" }\n");
+ String targetId = findTargetId(e);
+ if (targetId != null) {
+ w.write(" /** Activates this model instance in the current {@link \n"
+ + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
+ + "In case of using Knockout technology, this means to \n"
+ + "bind JSON like data in this model instance with Knockout tags in \n"
+ + "the surrounding HTML page.\n"
+ );
+ if (targetId != null) {
+ w.write("This method binds to element '" + targetId + "' on the page\n");
+ }
+ w.write(""
+ + "@return this
object\n"
+ + "*/\n"
+ );
+ w.write(" public " + className + " applyBindings() {\n");
+ w.write(" proto.applyBindings();\n");
+ // w.write(" proto.applyBindings(id);\n");
+ w.write(" return this;\n");
+ w.write(" }\n");
+ } else {
+ w.write(" private " + className + " applyBindings() {\n");
+ w.write(" throw new IllegalStateException(\"Please specify targetId=\\\"\\\" in your @Model annotation\");\n");
+ w.write(" }\n");
+ }
w.write(" public boolean equals(Object o) {\n");
w.write(" if (o == this) return true;\n");
w.write(" if (!(o instanceof " + className + ")) return false;\n");
@@ -1757,6 +1771,21 @@
}
}
+ private static String findTargetId(Element e) {
+ for (AnnotationMirror m : e.getAnnotationMirrors()) {
+ if (m.getAnnotationType().toString().equals(Model.class.getName())) {
+ for (Map.Entry extends ExecutableElement, ? extends AnnotationValue> entrySet : m.getElementValues().entrySet()) {
+ ExecutableElement key = entrySet.getKey();
+ AnnotationValue value = entrySet.getValue();
+ if ("targetId()".equals(key.toString())) {
+ return value.toString();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private static class Prprt {
private final Element e;
private final AnnotationMirror tm;
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/spi/Proto.java
--- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java Thu Dec 18 10:44:48 2014 +0100
@@ -193,7 +193,21 @@
* of the current model to the body element of the page.
*/
public void applyBindings() {
- initBindings().applyBindings();
+ initBindings().applyBindings(null);
+ }
+
+ /** Initializes the associated model to the specified element's subtree.
+ * The technology is taken from the current {@link #getContext() context} and
+ * in case of knockout.js applies given bindings
+ * of the current model to the element of the page with 'id' attribute
+ * set to the specified id
value.
+ *
+ * @param id the id of element to apply the binding to
+ * @since 1.1
+ * @see Technology.ApplyId
+ */
+ public void applyBindings(String id) {
+ initBindings().applyBindings(id);
}
/** Invokes the provided runnable in the {@link #getContext() context}
diff -r d06b9f7976d1 json/src/main/java/org/netbeans/html/json/spi/Technology.java
--- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java Thu Dec 18 09:22:47 2014 +0100
+++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java Thu Dec 18 10:44:48 2014 +0100
@@ -173,4 +173,22 @@
*/
public void valueHasMutated(D data, String propertyName, Object oldValue, Object newValue);
}
+
+ /** Apply technology bindings at selected subtree of the HTML page.
+ * Can be accessed via {@link Proto#applyBindings(java.lang.String)} or
+ * via method applyBindings(String)
generated when one
+ * is using the {@link Model} annotation.
+ *
+ * @param
+ A particular DOM subtree
+ that a knockout.js model gets
+ applied to can be selected by using
+ {@link net.java.html.json.Models#applyBindings(java.lang.Object,%20java.lang.String)
+ Models.applyBindings(m, id)} with an id of an HTML element.
+ There is new Model.targetId() attribute. It it is used the generated
+ applyBindings
method will bind to specified target id.
+ If the attribute is not specified, the applyBindings
+ is not generated. One can still use Models.applyBindings
+ to bind such model, however.
+
Memory model when using Knockout bindings has been improved (required additions of two new methods: {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and