Urigo / angular-meteor

Angular and Meteor - The perfect stack
https://www.angular-meteor.com/
MIT License
2.36k stars 621 forks source link

Call Angular Function From Inside Ng-Include #333

Closed nspangler closed 9 years ago

nspangler commented 9 years ago

Is it possible to directly call an angular function say $scope.test() directly from within a meteor template embedded in the angular .ng.html? Right now I set a Session in my meteor template and watch the Session for changes in my .ng.html. However it seems hacky. Is there a way to achieve this functionality directly?

netanelgilad commented 9 years ago

That is an interesting notion. I think we can add a helper to the template being rendered that gives access to $scope. That should give access to any properties on the scope for use in the Meteor template (be it functions or properties). Though I don't have much experience with Meteor templates. Does that make sense?

Urigo commented 9 years ago

@netanelgilad I might be wrong but I think @nspangler is trying something much simpler, calling a scope function in a regular angular template (.ng.html). If it is true, then of course it's possible..... @nspangler can you share the code that doesn't work for you please?

nspangler commented 9 years ago

@netanelgilad , @Urigo , no what @netanelgilad described is exactly the functionality that I desire. His explanation does make sense on how to achieve it. I am much more of an meteor guy than angular guy so I can assist on the meteor side as much as is needed. I think that providing a helper for the template being rendered is a good solution.

Urigo commented 9 years ago

@nspangler I want to work on it now, can you please provide an example code that you would want to use after this feature is added?

nspangler commented 9 years ago

@Urigo , @netanelgilad
Say I have a function declared in a controller:

  $scope.foo = function (foo) {
           console.log(foo);
   }

In my meteor template that is included in the ng.html that the above controller controls, I can still call the function like so:

     Template.meteorTemplate1.events({
          'click .logFoo' : function () {
                  foo("Logged foo");
          }
       });

where the function foo() is the function declared in the angular scope.

This might seem like a pointless endeavor to most people, however when I want to integrate my meteor templates with angular/angular-dependencies such as ionic, I need to be able to achieve such functionality. For example instead of having my $scope.foo() just log, lets replace it with an ionic alert. So that now the click in the meteor template on .logFoo can display an ionic alert. As of right now without such an enhancement, I set a Session and have my angular template watch the session for a change. When a change happens and is observed in the $scope the function fires. Does that make sense? I am more than happy to help in building such functionality.

MilosStanic commented 9 years ago

@nspangler Please try using "controller As" syntax. You can read all about it here: http://toddmotto.com/digging-into-angulars-controller-as-syntax/

But applied to your example, say you have it defined like this:

angular.module('yourModule').controller('yourCtrl', [ '$meteor', function($meteor){
  var vm = this; 

  vm.foo = function (foo) {
           console.log(foo);
   };

}]);

Then in your view you would have declared somewhere

<div ng-controller="yourCtrl as yc">
...
</div>

And then in the included template you would be able to call the function like this:

Template.meteorTemplate1.events({
          'click .logFoo' : function () {
                  yc.foo("Logged foo");
          }
       });

Please note yc. prefix to your function call. (I think there is a missing foo param in the function call)

Note: this is just theory. I haven't tested this, but from what I read about "controller As" syntax, this is exactly the type of thing that would be possible unlike in the classic approach.

Please try this, and let us know.

Urigo commented 9 years ago

OK, so the idea is to add a helper to the rendered template. It should be done here and we have easy access to both the template and the scope: https://github.com/Urigo/angular-meteor/blob/master/modules/angular-meteor-template.js#L65-L66

maybe using registerHelper function

@barbatus I wonder what are your thoughts on that

MilosStanic commented 9 years ago

@Urigo yes, that would be very useful for the purpose of integrating standard Meteor packages with angular-meteor. I thought this already existed but I somehow failed to find it in examples. :) So, definitely +1 for this feature from me.

barbatus commented 9 years ago

@Urigo As it was mentioned here, I also see ng-controller the only good way to do it. So we assign any controller into the directive and then, inside of the directive, take controller instance and assign it to, say, $ctrl property of a template instance. In the events helper we'll be able to use as:

Template.meteorTemplate1.events({
          'click .logFoo' : function () {
                  var $ctrl = Template.instance().$ctrl;
                 $ctrl.foo("Logged foo");
          }
       });
Urigo commented 9 years ago

@barbatus yes, I wouldn't want to give up on the controller-as syntax.

Urigo commented 9 years ago

@nspangler @barbatus @netanelgilad @MilosStanic check this solution and the examples. It keeps the $scope updated inside Template.instance(). This is pretty crazy, writing Angular controllers with Blaze templates....

Thanks @stubailo for the help!

After feedback I'll add tests

barbatus commented 9 years ago

@Urigo So now it will re-render every time after $apply even if parties is not changed?

Urigo commented 9 years ago

@barbatus good point, I'm not sure as I don't know what Blaze is doing if the data it's getting is the same? But also, I haven't found a better way to watch all of the scope and not specific parts of it.

Urigo commented 9 years ago

@barbatus ok I've found a solution, check this out: https://github.com/Urigo/angular-meteor/commit/532880b9459c883947457e875802a828e5f3b66d

barbatus commented 9 years ago

@Urigo It looks cool but messy, but If it works it's up to you what to do with it ultimately. Potentially another one approach could be to copy scope with angular.copy and then compare prev and new one with angular.equals, not sure though if it works.

Urigo commented 9 years ago

@barbatus have you looked at the latest solution in the latest pull request? https://github.com/Urigo/angular-meteor/pull/457

barbatus commented 9 years ago

@Urigo ah, you reverted it? Ok, then.

Urigo commented 9 years ago

:) yes the getReactively seems like a cleaner solution