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();
};
Related
I am using AngularJS 1.5 and using ‘$emit’ to send an event to parent controller to refresh parent controller data. On ‘$On’ I have written the logic to refresh the parent controller data.
Now, Parent controller data is being updated but after that it is unable to bind data for the child controller from where ‘$emit’ was triggered.
I tried to use ‘$apply’ but it is saying that ‘$digest’ is already in progress. I have also use Batrang tool to view the data and it is showing that page having all those data but it is not being displayed on UI.
Can anybody tell me how to force angular to bind those data with HTML Control which is already available on the page.
I cannot put sample code here because it's a live project & I'll have to create a sample project to replicate the issue. Even though If it is not easy to answer my query without sample code then I will put sample code on Plunker in a day.
Based on Angular documentation, there are two methods to declare controller in HTML:
one binds methods and properties directly onto the controller using ng-controller="SettingsController1 as settings"
one injects $scope into the controller: ng-controller="SettingsController2"
The second option is more common in the Angular community, and is
generally used in boilerplates and in this guide. However, there are
advantages to binding properties directly to the controller and
avoiding scope.
Using controller as makes it obvious which controller you are
accessing in the template when multiple controllers apply to an
element. If you are writing your controllers as classes you have
easier access to the properties and methods, which will appear on the
scope, from inside the controller code. Since there is always a . in
the bindings, you don't have to worry about prototypal inheritance
masking primitives.
So you could always refer to parent scope in child scope by using controller as:
<div ng-controller="parentController as parent">
<span>{{parent.title}}</span>
<div ng-controller="childController as child">
<span>{{parent.title}}</span>
<span>{{child.title}}</span>
</div>
</div>
I'm having a problem with the scoping of this inside of an angular-ui bootstrap modal. When I run the following code outside of a modal I have no problems with it:
var GlobalVariable = GlobalVariable || {};
(function(){
doSomethingWith(GlobalVariable.field);
})();
When called outside of the $modal GlobalVariable.field is perfectly accessible. As soon as I run this code inside a $modal, GlobalVariable.field is undefined. Now I can fix this problem by directly accessing window.GlobalVariable.field inside the $modal dialog but the problem is that the doSomethingWith method in this case is a 3rd party library which needs access to the global variables. I'm not about to go through all the third party libraries and add "window." on to all of the globally scoped variables.
I know that when I call $modal.open passing in some HTML that it gets mangled through the angular $compile function (which I presume eval()s the HTML as part of the process). My guess is that when $compile processes it, it assigns it a fresh this object meaning we don't inherit all of the globally scoped variables from window.
Is there any way that I can get angular's $compile to force 'inheritance' of the window's global variables all the way down to my modal?
Have a look at this Plunker that I generated from the Angular-UI bootstrap documentation:
http://plnkr.co/edit/jMcvTBDcrkPU7p4gFejT?p=preview
resolve
As you can see, the modal creates a new scope, so anything you want to use inside that scope must be passed in using "resolve" and returned. There's no need to force inheritance when you can just pass whatever you need in.
I'm making a dynamic web page with angular, the content from the main page should change, but to avoid writing too much code i decided to make it generic, but to know what type of content is being requested i need to send this parameter from a link/button with a ng-click, this would'nt be a problem but when i have to change the controllers i can't read the parameter.
ng-click="name='Name change'"
Here i'm trying to change a $rootScope variable named name, i tried
ng-click="$rootScope.name='Name change'"
even with a service function, but looks like doesn't work (i don't know too much about angularjs so i tried )
ng-click="$service.cambiarTipo='Name change'"
i made a plunker http://plnkr.co/edit/1BN76SbUAHuOSHs02gpL?p=preview
If you check the console log, you will see that the variable it's undefined, obviously if i change $rootScope.name from a controller i can see it from the other controller, but that's not useful since i need that feed from the user not the controller.
How i can change a rootScope variable from html?
Here'a one using a shared service between the two controller without using $rootscope at all. http://plnkr.co/edit/maKNHgVH20GxTJeCEveh
Note that ng-click is calling the service function. I'm assuming the function is for changing the name.
ng-click="service.cambiarTipo('Name change')"
You really shouldn't be using $rootScope all that much, let alone modifying it from the template. With that said, you can assign $rootScope to a $scope variable and access from the template like normal scoped variable. plunker
Controller:
$scope.rs = $rootScope
Template:
rs.name = 'Name Change'
I would like to reiterate that this is not something you should be doing as it goes against the angular way.
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
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/