Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Observable helper function with an array out of view.data or context #148

Closed
iboware opened this Issue · 19 comments

2 participants

@iboware

Hello,

I know we can use observable helper functions with "depends" property to observe changes in multiple properties like in this example: http://borismoore.github.com/jsviews/demos/features/observability/computed-helper.html

I have lots of view and all of them has their own seperate context and helper functions, also I have global helpers. I want to declare a global helper function which returns an observable array that is in the global context with a filter for binding it to select controls.(like users etc.)

Is it possible to do something like this, without referencing or cloning the array into view data or view context? If not, is it possible to give a separate context to an helper function?

Example:

userlist.depends = entityList.Users;
@BorisMoore
Owner

You can pass the same array, and bind to it, yes. You can have a helper that returns that same array in different contexts. Is there a particular missing piece the is preventing you from achieving this?

Helpers can be declared on templates, too, or passed in as options, etc. so there are many ways to have helpers for specific contexts, not just global helpers.

@iboware

I'm trying to do something like this but seems to not working. Is this approach appropriate?

        <select class="entitySelect" data-link="Value">
            {^{for ~GetEntityList(TypeID)}}
            <option data-link="{:EntityID:} html{:EntityName}"></option>
            {{/for}}
        </select>
var getEntityList = function (entityType) {
 return $.grep(entityData, function (entity) { return entity.EntityType === entityType; });
};
//entityData is a dynamic observable list.
getEntityList.depends = entityData;
$.views.helpers({
GetEntityList:getEntityList
});
@BorisMoore
Owner

Did you look at all the different examples of binding to select?. Two-way binding needs to be on the select element, not on the option. So this is definitely incorrect: data-link="{:EntityID:} html{:EntityName}". It is the select that has a change event...

@iboware

I think, it's not about binding. I want to get the select element populated with updated data after "entityData" array has modified. This binding is not working too. Isn't it about {^{for ~GetEntityList(Type)}} ? If not I'm really doing something seriously wrong.

        <select class="entitySelect" data-link="Value">
            {^{for ~GetEntityList(Type)}}
            <option value="{{:EntityID}}">{{>EntityName}}</option>
            {{/for}}
        </select>
@BorisMoore
Owner

OK - got it. (You had said it was not working, without saying what aspect wasn't working), For the array changes and the {^{for}} binding, I'll try to look more closely at you scenario, when I can get to it. There may be an issue - I'll need to investigate...

@BorisMoore
Owner

The issue is with putting GetEntityList.depends = entityData; or userlist.depends = entityList.Users

foo.depends=xxx should be passed a string (the observed path), or an array of strings, or and array which alternates objects and strings or sequences of strings, such as [myObject, path1, path2] (which says "observe path1 and path2 on my object). In your case, for the second example, you want to observe entityList.Users - so you can choose any of the following

userlist.depends = [entityList, "Users"]  

which will also work if you swap the Users array

userlist.depends = [entityList.Users, ""]  

which will only listen to the array changes, not to setting a new set of Users on the entityList .

If entityList is either a helper or is on the current data item you could also do userlist.depends = "~entityList.Users" or userlist.depends = "entityList.Users"

@iboware

Hello again. I was investigating this issue too. I found out this binding in one of your examples and it's working:

//this works
    helpers.GetEntityList.depends =[entityData,"*"];

//this does not work.
    helpers.GetEntityList.depends =entityData;

I don't know why it is working when I did declare it as an array

Is this the correct way? Also I have another problem about this, when dependent array changes, selected value resets itself. (lists selected element becomes the first one "- choose -")

Note: Sorry I saw your comment after I wrote this but the selected element position still resets itself.

Second Note:

//this approach did not work. entityData is an array of object which includes 4 string properties.
    helpers.GetEntityList.depends =[entityData,""];
//but this one works perfectly except the position reset issue.
    helpers.GetEntityList.depends =[entityData,"*"];
@iboware

Is there any way to trigger select element to make the correct option selected after observable array changed?
<select class="entitySelect" data-link="Value"> observable for with options </select>

I tried this to reach view data to trigger "#data.Value" but I couldn't reach view data.

    helpers.GetEntityList.depends = [entityData, "*", function (value) {
       //value has the entitydata array, and "this" has global context.
    }];
@BorisMoore
Owner

Thanks, I'm looking into these questions. I think there is some clarification needed and maybe a bug fix. I'll let you know, and give explanations after I have investigated more...

@iboware

Thank you, this "depends" feature saved me from using lots of jQuery bindings to manually fill select elements.

@BorisMoore BorisMoore referenced this issue from a commit
@BorisMoore Commit 30. (Beta Candidate)
This provides a fix for #89, #116, #148 and #149.
It is also a candidate for Beta. If no significant new bugs are
encountered then it will be used to provide an official beta version.
86e44a4
@BorisMoore
Owner

Can you test with the latest update? (Commit 30). It should be working now...
Thanks...

@iboware

Sorry, but it does not work, still resets the position to the first option. Is there something wrong with my template?

        <select class="entitySelect" data-link="Value">
            {^{for ~GetEntityList(Type)}}
            <option value="{{:EntityID}}">{{>EntityName}}</option>
            {{/for}}
        </select>
@BorisMoore
Owner

There are several aspects:
1. binding to entityData changes, so that a different array is returned when appropriate. For that you can put GetEntitiyList.depends = entityData - which should work now.
2. binding to observable collection changes on the array itself. Currently deep binding in paths that include function calls, such "a.b()^c.d" or "a^b()c.d" are not completely supported. Even "a.b()" when a.b() returns array does not yet automatically bind to collection change on the array.
3. if you return a new array from GetEntityList, then the options will all get re-rendered, so it is normal that selection returns to the first item, since the select will fire a change event when its content is removed, prior to insertion of the new set of options...

For 1) and 2) I hope this will all get fully supported in V1, but probably not yet in beta...

@iboware

Number 3 is exactly matching with my scenario. I'm using jQuery's grep function to filter the observable array, so it returns a new array, I think. Which method would you prefer in a scenario like this? Tag or something else?

Test Result: You said, "For that you can put GetEntitiyList.depends = entityData - which should work now." it does not work but GetEntityList.depends =[entityData,"*"]; is alright for me now.

@iboware

I fixed the position reset issue by binding data to the selected attribute of options. It's working as I expected now:

<select class="entitySelect" data-link="Value">
{^{for ~EntityList(~FieldValue(FieldID))}}
<option data-link="selected{:EntityID===#parent.parent.data.Value}" value="{{:EntityID}}">{{>EntityName}}</option>
{{/for}}
</select>
@BorisMoore
Owner

@iboware:The latest commit (35) should fix some of your original issues you had and 1) 2) 3) above. Also, take a look at the updated sample here: https://github.com/BorisMoore/jsviews/blob/master/demos/features/observability/observing-paths.html#L45 for an alternative way of doing dynamic binding to a list of options.

Let me know if you see issues still. If not we can close this issue...

@iboware

Sorry, I double checked everything but 1 and 3 still does not work.
1 - javascript helpers.GetEntityList.depends =[entityData,"*"]; works but javavscript helpers.GetEntityList.depends =entityData; does not.
3 - I tried the same option binding in your example but it does not work.

<option data-link="{>EntityName} value{:EntityID}"></option>

if I put selected attribute binding than it works

<option data-link="{>EntityName} value{:EntityID} selected{:EntityID===#parent.parent.data.Value}"></option>
@BorisMoore
Owner

Thanks...

For 1), when you say it works or not, this is for declaring a dependency to listen to collection change. That is not yet supported, see #158 (This comment). Your helpers.GetEntityList.depends =[entityData,"*"] is in fact listening to propertyChange on the array from GetEntityList, for any property, and that includes length. helpers.GetEntityList.depends =[entityData,"length"]will trigger update for collection changes that involve a change in length, so will indeed work for insert and remove, but not always for replace.

For 3) yes, for initial selection to work you can bind selected. I have updated https://github.com/BorisMoore/jsviews/blob/master/demos/features/observability/observing-paths.html to set the initial selection to the first item, using this.

So from my point of view these are working as expected, at this point...

@iboware

Yes, I got it now. As I pointed out in the #158 there are some workaround methods that does not cost much. I'm closing this issue.
Thanks. :)

@iboware iboware closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.