I am trying to broadcast a message from legacy javascript function into Angular controller(s) via a Angular service. Broadcasting works fine when called within Angular, but not outside.
See http://jsfiddle.net/yh3Ds/24/
It seems that angular.injector() is creating new module instance and
it is independent from the module instance bound with the html via ng-app attribute.
So solution is to use manual bootstrap and obtain the injector there.
Use this injector you can access the service instance and $rootScope which
is actually controlling the view.
Here is the updated fiddle; http://jsfiddle.net/d8vX3/1/
Related
Why does onclick="window.history.back()" work and angular's ng-click="window.history.back()" doesn't?
You can make this work adding window to your $scope, or even better to $rootScope so every $scope has access to window and thus your initial attemp would work as you've expected.
Example adding it to $rootScope:
<script>
app.run(['$rootScope', function($rootScope) {
$rootScope.window = window
}])
</script>
Then you just call:
<button type="button" ng-click="window.history.back()">Go back</button>
or:
<button type="button" ng-click="window.alert('it works!')">Alert!</button>
or whatever variable or function in global javascript scope you want.
onclick is an javascript event, so it can call function in javascript window object.
Where as
ng-click is an angular directive, which can only call functions which is available in the $scope. window is not available in $scope.
because in the template you use the controller or directive scope. So angular expects the scope object to have a property named window instead of searching for it in the global scope of the script
onclick="window.history.back()" works because it is vanilla JavaScript and onclick is an event on the element.
ng-click="window.history.back()" doesn't work because ng-click is an angular directive and angular is looking for an object called window on the controller's scope.
You can gain access to the window object, in angular the suggested way is to declare a dependency on the $window service and do any operations required on the $window object.
Reasons to use $window service: it helps to do unit testing when using $window service and not the global window object.
EDIT: below info used from Pro AngularJS book - chapter 19
Why and When to Use the Global Object Services
The main reason that AngularJS includes these services is to make testing easier. I get into testing in Chapter 25, but
an important facet of unit testing is the need to isolate a small piece of code and test its behavior without testing the
components it depends on—in essence, creating a focused test. The DOM API exposes functionality through global
objects such as document and window. These objects make it hard to isolate code for unit testing without also testing
the way that the browser implements its global objects. Using services such as $document allows AngularJS code
to be written without directly using the DOM API global objects and allows the use of AngularJS testing services to
configure specific test scenarios.
onclick is an js event, so it can call function in javascript window object.
But
ng-click is an angular directive, which can only call functions which is available in the $scope. window is not available in $scope. In angularjs we can do this by:
ng-click="doTheBack()"
$scope.doTheBack = function() {
window.history.back();
};
Suppose my angular js app(myApp) has a controller and directive, How would I be declaring both?
angular.module("myApp",[])
.controller("myController"......
angular.module("myApp")
.directive("myDirective".......
OR
angular.module("myApp",[])
.directive("myDirective".......
angular.module("myApp")
.controller("myController"......
If you see the above code, I am defining my angular app on a controller, and then just retrieving it for the directive. In the second case, I am defining my app for a directive, and retrieving it for a controller.
Which is the right way? How to decide on what is the correct way?
Both are correct. It doesn't affect the way the application is bootstrapped.
You only need to understand what angular.module() does.
angular.module("myApp",[]) creates a new AngularJS module named "myApp" with the dependencies passed as the second parameter.
angular.module("myApp") is a getter. It returns the module if it has been created before.
Both return the same object. So then, you can add your directives and controllers in the order you want.
Once everything is loaded, AngularJS starts the bootstrapping process. See the AngularJS documentation.
I'm turning a piece of a non-angular web page into an Angular version by angular.bootstrapping it with an Angular module. It works great except for the fact that it needs to communicate with the other parts of the page. Unfortunately I cannot convert the rest of the page to Angular at this time.
The best way I've figured to do this so far is by firing vanilla JS events on the bootstrapped element containing the information I need to communicate.
Are there established patterns or better ways of doing this?
If you need to send a message from vanilla javascript to an AngularJS controller, you can access the scope like this:
var ngScope = angular.element('[ng-controller]').scope();
// or choose a more appropriate selector...
Then, you can do whatever you want on the scope such as broadcast an event or even just invoking a function:
ngScope.$broadcast('myEvent');
ngScope.$apply(ngScope.controllerFunction());
Sending a message from Angular to vanilla javascript should be much simpler. Simply invoke a global event or just access a global function/property.
I had the same problem when I started to migrate the project I am working on to Angular. I had to communicate on different type of events like clicks with my non angular app. If you would like to use a known design pattern you can go with "Mediator pattern". It is a publish/subscribe based communication. You can have a singleton global instance of the mediator and access it from both angular and your non-angular app
Here's a link for the lib: http://thejacklawson.com/Mediator.js/
Before your app (both of them) loads you can do something like this:
window.mediator = new Mediator();
Later you can have in your Angular app a service like so:
angular.module()
.service('mediator', function() {
var mediator = window.mediator || new Mediator();
return mediator;
});
And from your non-angular app you can simply do:
mediator.subscribe('something', function(data){ ... });
And from your Angular controller or whatever you have you will inject the mediator service created and use it like so:
mediator.publish('something', { data: 'Some data' });
Now you have both an Angular and non-Angular way of communicating between your apps.
This solution did wonders for me.
$on only listen for angular event such as $braodcast & $emit only and they will work within bootstrapped app module only.
If you want to listen jQuery events then it should use .on/.bind as like we do in jquery like $('selector').on('event',function(){});
If multiple events are associated with single DOM element then prefer Directive would be the better way get track of element event.
Directive:
app.directive('myCustomer', function() {
return {
restrict: 'E',
compile: function(element,attrs){
element.on('click focus blur',function(){
//code here
});
}
};
});
Otherwise you can bind event listener on specific element, then you can bind it by taking help of jQLite using $document, $window, $element(Provides controller level element) dependency.
Controller
$document.find('.myClass').on('click',function(e){ //placing on specific element.
//code here
});
Plunkr Example
Hope this could help you, Thanks.
I'm building an application based on AngularJs and GMaps API v3. The issue I've came up with is that I need a way to call methods of the directive's controller from another directives (that's easy with require) and from the same directive.
Let's put it in an example:
I've got a directive that renders the map using GMaps JS lib. and also render some markers and also I've got another directive that handles navigation (i.e. Changing routes). When I go from route A to route B I need to erase the markers and leave the map blank.
So which is the best way to achive this? Should I build three directives? One with all the render methods and destroy markers methods and then call them from the other directives? Or is there a way to inject the directive's controller into the same directive?
So I've googled a bit an I've found this!
https://github.com/bennadel/AngularJS-Directive-Controllers/blob/master/app/directives/master.js#L11
Just like you said #musically_ut the controller is executed before the link function so it's could be injected into the link func. and make it's method available.
The controller for a directive executes before the link function and all $scope declarations made in the controller are available on the scope of the same directive.
Hence, the controller is injected into the directive itself.
Demo: http://plnkr.co/edit/HnwJ0w0VTLbNOC87k74n?p=preview
My understanding is that when I run
App.CheeseController = Ember.Controller.extend({ type:"brie"});
A class CheeseController is created and that when I activate the Cheese route an instance of that class is made which is what I actually touch when talking to the controller in my handlebars template.
Is it possible to directly access that instantiated object from within the javascript console (or from within my program)? More generally, where do the objects that Ember automatically makes live?
A class CheeseController is created and that when I activate the Cheese route an instance of that class is made which is what I actually touch when talking to the controller in my handlebars template.
Yes that is exactly what happens. Ember creates a singleton instance of App.CheeseController and provides it as the context when rendering your handlebars template.
Is it possible to directly access that instantiated object from within the javascript console
Yes. The best way to do this from the javascript console is by using the handlebars {{debugger}} helper from your template. This will open the JS debug console in the context of your template.
<script type="text/x-handlebars" data-template-name="cheese">
{{debugger}}
</script>
With debugger open, you can access the instantiated controller singleton as this, so this.toString() should return something like <App.CheeseController:ember225>.
(or from within my program)?
Depends on which part of your program
From a route: Use this.controllerFor('cheese')
From a model: No. Please don't access controllers from models.
From another controller: If you declare a dependency in the other controller, needs: ['cheese'] then the singleton App.CheeseController will be accessible from the other controller via the property controllers.cheese. See Automatically synthesize the controller 'needs' dependencies
From a template: Use the needs array to declare a dependency from the templates controller, then from within your template the cheese controller is at: {{controllers.cheese}}
It is also possible to access the cheeseController instance via the ember container, but please don't. The container is not meant to be a public API. Recent updates to Ember have made accessing it somewhat awkward. This is because using global constants to access instances breaks encapsulation, and while that is fine for the console it should be avoided in your application code. For more detail, see App.container was not meant to be a public API
More generally, where do the objects that Ember automatically makes live?
Internally ember caches controller singletons in the container. Of course it is not a part of the public API, but if you are curious about how things work internally check out container_test.js and
What is the purpose of the Ember.Container