Angularjs how to change $rootScope variables from UI - javascript

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.

Related

AngularJS Child controller data not loading on UI while using $emit to refresh Parent data

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>

Angular ng-click with inline function

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();
};

AngularJS: pass variable from backend while loading a page

This is my first ever trial with Angular JS and am stuck at a very basic step. I have an AngularJS front end and Grails backend. Below are the code snippets I have followed by my question.
URL Mapping entry:
as
"/graph"(controller: 'Graph')
This is my Grails controller/action which renders the GSP:
as
class GraphController {
def index() {
render(view: "../graph", model: [employeeId: "197040"])
}
}
This is the AngularJS file, which is saved as graph.gsp:
When I give the URL: hostname:port/graph I am able to see the body displayed as "Hi ! Welcome". But, I couldn't figure out a ( simple ) way to read that employeeId and display it as "Hi ! Welcome 197040" ( i.e being able to read that variable employeeId sent from backend)
<div><h3>Hi ! Welcome {{employeeId}}</h3></div> didn't work as expected.
I am sorry if this is too basic a question, but I just couldn't see the answer anywhere.
Try this:
<div ng-init="employeeId=${employeeId}"><h3>Hi ! Welcome {{employeeId}}</h3></div>
This will assign the value of the employeeId within the context of the GSP to a variable available within the context of Angular. To understand what I mean, take a look at the generated HTML source in your browser.
By doing this, the employeeId variable will be assigned within the scope of your Angular controller also.
There is a good tutorial on using Angular with Grails here http://claymccoy.blogspot.com.au/2012/09/grails-with-angularjs-and-coffeescript.html?m=1
Your Angular app runs on the user's browser and has no access to your backend environment. Therefore simply setting employeeId as a backend environment variable does not allow Angular to access it.
I can recommend two solutions to this problem:
1) In the index view (/graph), render employeeId as a JavaScript global in a script tag. In Angular, register the global employeeId as an Angular value. Then you can inject the employeeId value into your Angular controller and render it in the Angular view.
2) Retrieve the employeeId asynchronously via Ajax before your Angular app bootstraps, and register it as an Angular value. Then you can inject it to your controller. I recently wrote a blog post about how this can be done: http://biodegreeprogrammer.blogspot.ca/2014/07/pre-loading-data-asynchronously-in.html
The other thing here is, this is gsp afterall.. and your grails variables will work as per normal like how you defined the layout..
<div><h3>Hi ! Welcome ${employeeId}</h3></div>
Which will be grails pushing that info back....
The alternative is something like this: as shown per service on this demo site..
https://github.com/vahidhedayati/testingarrested/blob/master/grails-app/assets/javascripts/testingarrested/arrestedServices.js
Maybe you wish to try out arrestedplugin for yourself as a demo and then take the for example above service and change it for your own usage..

Inject Directive's Controller inside the same Directive

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

AngularJS: Reference to controller dynamically (by name)?

Working on a module based app where depending on the user, I'll load a given template (view) as a module inside a common view. The problem is that the different views require different controllers and they one share a small set of common form inputs.
Based on a call to my server I'll get a JSON response containing what view/controller should be loaded for that user. This solution worked fine earlier as all my controllers were in the global scope:
$scope.corporation.payloadController =
// Contains the String "ComputerPayloadCtrl"
[window]data.corporation.payloadController;
Now however, after I have rewritten the applications to use the angular module design pattern, I get the following error (I no longer use [window]):
Argument 'corporation.payloadController' is not a function, got string
The controller is already defined, so I'm only looking for a way to reference it by String.
.controller('ComputerPayloadCtrl', ['PayloadService', '$scope',
function(PayloadService, $scope) {
$scope.payload = PayloadService.payload;
}])
The more I work with this problem, the more this entire approach I've chosen is bugging me. So if anyone has any suggestions on how to alternatively solve this I'll gladly hear it.
Edit: So I found a very simple workaround, but I'll let the question stand in case there is an actual way to do this.

Categories