Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic data-binding with an absolute path do not work #319

Closed
codler opened this issue Dec 13, 2013 · 5 comments
Closed

Dynamic data-binding with an absolute path do not work #319

codler opened this issue Dec 13, 2013 · 5 comments
Milestone

Comments

@codler
Copy link
Member

codler commented Dec 13, 2013

In this example http://jsfiddle.net/7KfEe/

    {{#items:i}}
    <label>Enter your name: <input value='{{items[i].name}}'></label>
    <p>Hello {{i}}, {{items[i].name}}!</p>
    {{/items}}

    {{JSON.stringify(items)}}

When writing in the input field, the text below should have changed, but it doesnt.
I could change {{items[i].name}} to {{.name}} then it will work. but in my case it need to have an absolute path because I have two different data set with the same structure, where the absolute path actually points outside the loop.

@Rich-Harris
Copy link
Member

This is a tricky one. Basically, items[i].name is an expression rather than a straightforward reference, and expressions can only have one-way binding. If you had a situation like this...

<input value='{{ foo.toUpperCase() }}'>

...then we know that if foo === 'blah', the input's value should be 'BLAH'. But we can't go the other way, because

  1. we can't easily determine that the relevant part of the model is foo, and
  2. if the user entered 'BLAH' we couldn't possibly know if foo should be 'blah' or 'Blah' or 'bLaH' or whatever.

The problem with 1 is that reverse-engineering an arbitrary expression is just really difficult. The problem with 2 is that some expressions are not 'reversible'. If we could solve 1, then we'd know whether or not 2 applied to a given expression (like items[i].name).

It would be neat to have a solution to this problem - I think it's certainly possible to identify expressions like items[i].name that could be bound in both directions. Between those and the unsolvable cases like foo.toUpperCase() there's an infinite range of reversible-but-crazy-hard-to-reverse-engineer expressions like

<label>Circle area: <input value='{{ Math.PI * radius * radius }}'></label>

that probably aren't even worth attempting.

Either way we're left with an open question - what should happen when an input's value is bound to an expression? Maybe a console warning? Throwing an error seems like overkill since in a few edge cases one-way binding to an expression might actually be the desired result. Would be interested in any ideas.

In the meantime, none of this solves your problem. I think the most likely solution is to fire events and listen for them:

<input on-input='changeName:{{i}}'>

Hope this helps.

@codler
Copy link
Member Author

codler commented Dec 14, 2013

Would it be possible to treat {{items[i].name}} as a reference? So it check if it can be or is a valid reference otherwise it will be an expression?

When it is a {{name}} I see the _twowayBindings property have a keypath items.0.name

@codler
Copy link
Member Author

codler commented Dec 14, 2013

Futher looking.. When I look on how the template gets parsed with {{name}}

    "t": 4,
    "r": "items",
    "i": "i",
    "f": [" ", {
        "t": 7,
        "e": "label",
        "f": ["Enter your name: ", {
            "t": 7,
            "e": "input",
            "a": {
                "value": [{
                    "t": 2,
                    "r": "name"
                }]
            }
        }]
    }...

and with {{items[i].name}}

[{
    "t": 4,
    "r": "items",
    "i": "i",
    "f": [" ", {
        "t": 7,
        "e": "label",
        "f": ["Enter your name: ", {
            "t": 7,
            "e": "input",
            "a": {
                "value": [{
                    "t": 2,
                    "x": {
                        "r": ["i", "items"],
                        "s": "${1}[${0}].name"
                    }
                }]
            }
        }]
    }...

Would it be possible to convert the parser to output this instead?

"value": [{
  "t": 2,
  "r": "items.i.name"
}]

when that is possible we can go to next step and referrer to another items.

"value": [{
  "t": 2,
  "r": "otherItems.i.name"
}]

@Rich-Harris
Copy link
Member

The problem with that is that there's no way to tell, by looking at items.i.name, that the i is an index reference (as opposed to the i property of the items object). Instead of tackling it at the parser stage, we have to wait until the expression can be resolved at render time.

What I'm hoping is that it's possible, once an expression like items[i].name has been resolved to (for example) items.0.name, to reliably determine that it's a keypath that can be bound in both directions. My gut says it is possible, but I haven't tried to implement it yet! The fun starts when we have situations like users[id].name resolving to users["undefined"].name and so on... I don't think it will be totally straightforward.

I'm going to mark this as a 0.3.9 milestone, since it's a bit of an unknown quantity and I'm planning to get 0.3.8 out the door ASAP

@Rich-Harris
Copy link
Member

This is solved as of c040824, at least for the original case where we have expressions like {{foo[i].bar}} - once the expressions resolve, we can do a check to see if they can be treated as regular keypaths, and if so then that's what happens.

It's a bit of a hack, but it works. I'll close this issue for now, as being able to reverse engineer more complex expressions is probably a bit ambitious. One day maybe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants