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

Data binding depends on js representation of datatypes #2

Open
davidsd opened this issue Oct 10, 2013 · 4 comments
Open

Data binding depends on js representation of datatypes #2

davidsd opened this issue Oct 10, 2013 · 4 comments

Comments

@davidsd
Copy link

davidsd commented Oct 10, 2013

This is mostly a conceptual issue that isn't too hard to work around in practice. AngularJS lets you bind data between templates and $scope using strings that are basically paths into JS objects. For example, if we have $scope.person = { age: 30 }, then a template can do ngModel="person.age". This means that users of angular-fay will have to be aware at all times of Fay's representation of data types as js objects (so they can write ngModel="person.data.age" or whatever), and that Fay will be tied to a specific representation or it could break everyone's templates. The only way I can I think of to avoid this pain point would be to modify angular's template parsing code.

@bergmark
Copy link
Member

I consider Fay's ADT representation as objects a part of the API so Fay would need a major bump to change this representation. I don't see any reason to change it either. I think it will be fine to depend on it. One thing that will change is that the instance fields in the json representation will change to contain fully qualified constructor names, but I don't think that will matter for the angular bindings.

Are these objects mutated, and if so will Fay perform these mutations? Can we bind new data when something changes to keep persistence? Depending on this we might want to either serialize objects through the FFI, or make sure that that these objects are only used in the Fay monad, perhaps using something like fay-ref.

@davidsd
Copy link
Author

davidsd commented Oct 10, 2013

Yes, angular mutates objects in a few different ways. For example, if data binding is used on a text input field, then the contents of that field are automatically copied into the referenced object whenever the user types something. (This could end up being tricky for type safety, for instance if an int or list value is bound to a text input field, then it'll get replaced with a js string as soon as the user types something.)

Another way angular mutates objects is when a template uses the ng-repeat directive to loop over an array of objects. As an optimization, angular stores a hash in each object to make it easier to keep track of when it's changed, and avoid redrawing the dom when it's unnecessary. For this reason, it's generally good to mutate data in place instead of replacing it completely, at least if that data is referenced in a template.

@bergmark
Copy link
Member

If you expect a number in an input field you should validate it before inserting it into the model, is validation built in to angular? You should probably also be using <input type="number"> for this case.

If the ffi functions to set these bindings use FayRef's (or maybe we want a custom AngularRef) we know that they will mutate in whatever way angular fancies and some property names may be reserved. We'd also need to clone a persistent value before we can let angular have its way with it.

I haven't thought this last part through yet, but perhaps we can also supply something like

modifyAngularRef :: (a -> a) -> AngularRef a -> Fay ()

to somehow modify the references in place, like recursively inserting the new a into the ref again, which would be idempotent if there is no change (but inefficient for large structures).

@btford
Copy link

btford commented Oct 24, 2013

would be to modify angular's template parsing code

Angular's DI system permits this. You could replace or decorate the $parse service which is used internally.

Creating a new $parse service that replaces the existing one:

angular.module('newFayngled.$parse', []).factory('$parse', function () {
  // ...
});

Decorating the existing service (this is tricky... probably easier to replace it with your own):

$provider.decorator('$parse', ['$delegate', function($delegate) {
  return function (context, locals) {
    // custom code under some conditions,
    // else fallback to angular's default implementation
    return $delegate.apply(this, arguments);
  };
}]);

Loading the new $parse (by listing newFayngled.$parse as a dependant module of your application's module):

angular.module('myApp', [ 'newFayngled.$parse' ]);
// ...

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

3 participants