BorisMoore / jquery-datalink

A data linking plugin for jQuery.
425 stars 42 forks source link

Feature request: populate the elements with the object members #7

Open warpdesign opened 13 years ago

warpdesign commented 13 years ago

From what I have understood, if you have this object:

var foo = { firstName: 'Nicolas', lastName: 'Dupont' };

And the following form with empty input elements:

<form> <input type="text" name="firstName" value = "" /> <input type="text" name="lastName" value = "" /> </form>

Doing $('form').link(foo) won't fill in the form with the foo members:

foo has "Nicolas" and "Dupont" values while the form elements don't have the same values...

Thus, we start with an inconsistent link: both the form and the object not having the same values unless the form is modified by the user.

A use case is for example when you retrieve the object's properties from database and want to fill in the form with it.

ghost commented 13 years ago

The process for this would be:

$('form').link(foo);

Then as the data comes back you set it on the object using the data() function:

foo.data("firstName", value);

This will then update the form with the values returned from your db query. Until the form and the object is not connected the required event handlers has not been registered and the link does not exists. So, if you know you are going to work with data you want to sync back to the UI, connect the two and then update the Object or vice versa.

Schalk

ghost commented 13 years ago

What I forgot to add is that it is very important to note that the JavaScript object needs to be a jQuery object so the line above should be:

$(foo).data("firstName", value);

jamiemthomas commented 13 years ago

The jquery.datalink API was designed to work in conjunction with the jquery.tmpl API, or with server rendering, or other approaches to generate the initial view of the data. The assumption in this case is that you will use a template to create the form, pre-filled with data, and then link the form to respond to changes.

Based on Scott Guthrie's posting in May (http://weblogs.asp.net/scottgu/archive/2010/05/07/jquery-templates-and-data-linking-and-microsoft-contributing-to-jquery.aspx), they are working on declarative linking that will somewhat merge these concepts and allow linking to occur naturally as a part of the templating process. I created a fork (http://github.com/jamiemthomas/jquery-datalink) that implements declarative linking consistent with Scott's posting. I also updated the contact demo to use declarative linking, so you can look at the html and js for the demo to see how your usage would change once this capability is released.

Hope this helps!

ghost commented 13 years ago

Nice one Jamie, I will definitely check this out.

DrZim commented 13 years ago

Make sure you use the latest JQuery. I believe the template plug-in uses JQuery to implement change events. Older versions of JQuery do this, but in an incorrect order. Likewise, make sure you use JQuery when changing the data.

Here's a good article http://expansive-derivation.ossreleasefeed.com/2010/10/its-the-datalink-that-binds-us-jquery-datalink/

ghost commented 13 years ago

Hey DrZim, thanks for linking to my article ;D

DrZim commented 13 years ago

LOL. Small world. It really is a good article. I am trying to figure out, since you can have nesting in templates, how you could have nesting in the linking. If you have a return object that has nested arrays, how to have templating "nest" the return data.

We are also trying to figure out how to flag the object data if the client changes it, sort of a "dirty" flag. That way we can Array.filter(), pull out a set of items to save, and send them to the server.

jamiemthomas commented 13 years ago

The contacts example included with the datalink plugin uses nested templates operating on an array of contacts, which in turn have arrays of phone numbers. In the example the linking is handled through iteration over the DOM elements after they are added to the page (aka very manually). In practical situations this would be too tedious and hard to maintain (and painful to write). However, the plan as noted by Scott Guthrie on his blog is to merge templating with linking to support a declarative approach within the template markup that would make this much easier. I demonstrated this in my fork by adding the declarative {{link}} tag and by modifying the {{tmpl}} and {{each}} tags to automatically cause nested templates to update when changes occur to the array (through array change events).

However, your question may be regarding how to make the tmplItem's returned reflect the hierarchial nature of the input data. Unfortunately, per the spec and the implementation, only the {{tmpl}} tag creates nested tmplItem instances. The {{each}} tag simply performs a templating loop and does not incur the overhead of creating child tmplItem instances. While I understand the rationale (performance versus function), the tag names and usage are both confusing and awkward. Both tags perform iteration over a set of values and render template content. However, the {{tmpl}} tag can only render a named template and the {{each}} processes nested content as the template. As a user of the API you are forced to create special named sub-templates in separate script blocks for each type of item you want to nest and produce tmplItem instances. I would prefer one tag {{each}} which supports both inline and named templates (feasible) with a flag or convention indicating whether to create a tmplItem instance during the nesting process.

Also, the update process for nested templates is inadequate. The most common case (90% case is seems) for nested templates that need to be updated would be adding an item to the array. As it is, you are only able to update the content for an entire tmplItem instance, so if you have an object with three child arrays your are editing, you will have to rerender the entire thing even if the change was adding a single item to one of the arrays. My preference here would be to support intelligent rendering of arrays, where the items in the array are linked to the tmplItem and tracked such that a change to the array (such as adding a new item) would only require rendering the content relevant to the change. If you add an item, only render content reflecting the new item and insert it appropriately relative to the other items. If you sort the array, reorder the elements in the DOM to reflect the underlying change. Maybe this behavior should not be automatic, but the performance of the current implementation is subpar when dealing with complex pages because of the requirement to regenerate so many of the elements to support these relatively simple operations. If you look at the updated contact example in my fork, you will notice I had to break the templates into separate script blocks to successfully implement declarative binding that did not require updating the entire page when a phone number was added or removed. Unfortunately, when you add or remove contacts, the entire page does have to be rerendered.

Lastly, to your comment regarding tracking of dirty objects, you could use special converters within your linking to be notified of changes and mark instances as dirty, but I recommend instead subscribing to the the "changeData" event using $(object).bind() to notify you when any value on an instance is being modified. Then you can use a generic approach to track dirtiness and as you said, filter the set before saving. In our code, we use this technique to support full change tracking and it works really well. However, until they add something comparable to the array change events from my fork (compliments of Dave Reed), you cannot fully track changes to a hierarchial model.

warpdesign commented 13 years ago

$('form').link(foo);

Then as the data comes back you set it on the object using the data() function: foo.data("firstName", value); What I mean is that this should happen automatically once the link has been established... thus I'm asking for an option to do that automatically :)

Once I add the link, it means my object has already been filled with the db data. So I thought the plugin would in turn populate the form with the corresponding data, before creating the link...

ghost commented 13 years ago

warpdesign, unfortunately the plugin does not work in this way currently. In order for a change to be pushed from the UI to the object the UI needs to trigger a change event and in order for the Object to push it's data back to the UI it needs to generate a changeData event.

Therefore, if possible in your use case, link the form to the Object before populating the Object with data from the DB. Then as you fill the Object with data the UI will be updated as well.

Just remember to use the $(object).data("key", "value"); syntax.

DrZim commented 13 years ago

In my humble opinion,

The original intent was to render content with links. In this way, the content already exists when establishing the link. Thus two problems exist if we auto copy data: 1) which direction should data be copied, especially if you have already rendered content and an empty object, and 2) if you already have rendered content, it would copy over it again, being redundant.

So, having a single responsibility of "just link the data" is actually pretty attractive. It does force a particular way of thinking which is certainly counter intuitive generally speaking, but the end result is manageable.

jeffmax commented 13 years ago

I understand the limitations of the current implementation, but it appears that the examples listed in the readme explicitly show the datalink plugin doing exactly what warpdesign was requesting. Specifically it implies that issuing a "changeField" event with no extra parameters would cause the form to be populated with the data from the objects. I have not seen this to be the case. From the "Updating immediately" section:

// or in reverse $(source) .link(target); $(target) .trigger("changeField"); alert($("[name=age]").val()); // target.age

DrZim commented 13 years ago

If we are trying to establish a norm for what happens when an html element and a data property link, knockoutjs seems to favor changing the html values to the JavaScript values upon activating.