-
Notifications
You must be signed in to change notification settings - Fork 2
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
Generating controllers via ffi templating #4
Comments
This might be an omission in the runtime for serializing the fay monad, i'll look into it! As a side note, calling the FFI from the strictness wrapper, pretty funky stuff :) But I think it'll work. |
I added that test case, but I also tested this one which doesn't work, it may be the same issue:
It might be the same as faylang/fay#267 too. Could you try making a smaller reproduction of this please? |
The following 3 files are intended to generate a minimal valid angular controller that is initialized via a function call. It fails wtih Error: Argument 'ConstCtrl' is not a function, got Fay$$Monad AngularConst.hs (build with "fay --package fay-text AngularConst.hs --strict AngularConst"): module AngularConst where
import FFI
import Prelude
constController :: Int -> Fay ()
constController = ffi "\
\ (function($scope) { \
\ $scope.val = %1; \
\ console.log($scope.val); \
\ $scope.getVal = function () { return $scope.val }; \
\ })" const.js: ConstCtrl = Strict.AngularConst.constController(7); index.html: <!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="AngularConst.js"></script>
<script src="const.js"></script>
</head>
<body>
<h2>Constant</h2>
<div ng-controller="ConstCtrl">
<span>Initialized with {{val}}, returns {{getVal()}}</span>
</div>
</body>
</html> The generated code is: AngularConst.constController = function($p1){
return new Fay$$$(function(){
return new Fay$$Monad(Fay$$jsToFay(["unknown"], (function($scope) { $scope.val = Fay$$fayToJs_int($p1); console.log($scope.val); $scope.getVal = function () { return $scope.val }; })));
});
}; By comparison, a working javascript version of const.js is: mkConstController = function (val) {
return (function ($scope) {
$scope.val = val;
console.log($scope.val);
$scope.getVal = function () { return $scope.val };
})
}
ConstCtrl = mkConstController(7); |
Try the second commit, it seems to do the trick. I have the same fay module as you:
This html:
Compiling with This logs |
Yes, that does load without errors, and if I modify the generated constController to log when it is called, it gets called. When loaded with Angular though, the generated code does not seem to be recognized as an Angular controller. The generated code is: > console.log(ConstCtrl)
function (){
var fayFunc = fayObj;
var return_type = args[args.length-1];
var len = args.length;
// If some arguments.
if (len > 1) {
// Apply to all the arguments.
fayFunc = Fay$$_(fayFunc,true);
// TODO: Perhaps we should throw an error when JS
// passes more arguments than Haskell accepts.
// Unserialize the JS values to Fay for the Fay callback.
if (args == "automatic_function")
{
for (var i = 0; i < arguments.length; i++) {
fayFunc = Fay$$fayToJs(["automatic"], Fay$$_(fayFunc(Fay$$jsToFay(["automatic"],arguments[i])),true));
}
return fayFunc;
}
for (var i = 0, len = len; i < len - 1 && fayFunc instanceof Function; i++) {
fayFunc = Fay$$_(fayFunc(Fay$$jsToFay(args[i],arguments[i])),true);
}
// Finally, serialize the Fay return value back to JS.
var return_base = return_type[0];
var return_args = return_type[1];
// If it's a monadic return value, get the value instead.
if(return_base == "action") {
return Fay$$fayToJs(return_args[0],fayFunc.value);
}
// Otherwise just serialize the value direct.
else {
return Fay$$fayToJs(return_type,fayFunc);
}
} else {
throw new Error("Nullary function?");
}
} This doesn't look like the function($scope) {..} that Angular is expecting. Also I see no mention of the captured 7, but I don't really understand what is going on :) |
That's the jsToFay code for functions, it partially applies the arguments it gets to the original fay function you are serializing. It's supposed to be called as a normal function. Can you tell why angular doesn't accept it? |
|
My guess is that Angular expects a function argument called exactly "$scope"; if I modify a javascript Angular controller and rename the $scope argument, Angular fails to load the controller. @btford is this the case, or do you have any suggestions about how we should interface with Angular? |
Ehm, i'm not sure what to think of this :) http://stackoverflow.com/a/12108723/182603 |
Oh, I forgot about this detail:
though i think this won't work in general. I have to think more about serializing actions before I know what the implementation should really be. |
@kfish Angular infers the name based on the function parameters, unless you add annotations. This is convenient when developing, because it frees you from having to maintain two lists of dependencies for a controller and keep them in sync. However, you probably want to avoid that in a case like this. :) function MyCtrl ($scope) {
// ...
} Is the same as: function MyCtrl (foo) {
// $scope aliased to foo
}.$inject = ['$scope']; More info here: http://docs.angularjs.org/guide/di#dependency-injection_dependency-annotation_-annotation |
that's exactly right. that can be mitigated when using explicit |
Thanks for the input! Happy to see that there are other ways of doing it :) I'm not sure if the other approaches will make much difference, what we have are functions generated dynamically by fay so we can't force structure on it (as in naming arguments or assigning properties to them). But like I said, I need to experiment some more with the serialization to see what it should actually look like. @kfish you can use what i wrote above for now at least. |
I thought a bit more about it, this only works here because we have the function as an ffi string. I'm not sure what your plans are, but if you try to create these controllers from normal fay code you will end up with unnamed functions and this won't work. We'd need to be able to tell angular "yes this is really a controller" somehow, or wrap the fay action through js or the ffi before passing it to angular. |
In commit 2956a4b (branch example-templating) I'm trying to generate Angular controller code via Fay ffi,
In src/Angular.hs there is:
which is called in examples/todo/todo.js:
However this fails with:
The goal is to generate all the Angular code required for a Controller (and later, for a Directive etc.) from a Haskell specification, so fay-angular would need to generate various javascript classes/functions that are structured in the required way, with argument name $scope etc. Any suggestions about where the best place to implement this would be? is it possible from within a fay library, or will it require some modification to the fay compiler itself?
The text was updated successfully, but these errors were encountered: