Can you access a variable from one scope to another in Angular - javascript

I have an Angular.js application that I am building. It is truly my first real Angular.js application, so I am learning real world issues as I go along and how to solve them.
My application is to be used by judges while presiding over hearings in the courtroom. It has the following views:
Calendar
Documents
Parties
It also has a Preferences screen where they set their default selections for courthouse, courtroom and type of law they normally work in.
My issue is with the preferences screen. I am needing to provide something like this:
<div>
<div>Civil</div><div></div>
<div>Courthouse</div><div><--dropdown of courthouses set to their default if selected already--></div>
<div>Courtroom</div><div><-- dropdown of courtrooms in the selected courthouse. Should only populate and be selectable after courthouse is selected--></div>
</div>
I already have code in another controller that grabs the courtrooms filtered by courthouse and type of law and would like to reuse that here. Is the best way to populate a variable in a factory and then refer to that in any of the controllers? Thus, I might have:
angular.module('DepartmentService', []).factory('DeparrmentService', ['$rootScope', '$route', function ($rootScope, $route) {
// Do stuff to populate the department here
return DepartmentService;
}]);
I could then do:
$scope.departments = DepartmentService;
Is that correct or is there another/better way to gain access to these variables across the various controllers in my application?
I know that using global variables at the rootScope is frowned upon, but it seems to me the easiest way would be if I could have a variable that doesn't go away when the page refreshes and is accessible to any controller.

You have a good basic idea. Something like this will be better, as a factory is a singleton. You should also always return promises from your services, where possible.
angular.module('DepartmentService', []).factory('DeparrmentService', ['$rootScope', '$route', '$q', function ($rootScope, $route, $q) {
var data;
populateData();
return {
getData:getData
};
function populateData(){
//get the data
data = responseFromServer;
}
function getData(params){
var deferred = $q.defer();
var filteredData = data.filter(function(d){
//do filtering here based on the params that you passed.
})
deferred.resolve(filteredData);
return deferred.promise;
}
}]);
In your controller you will call it like this:
angular.module('app').controller('MyController', function($scope, DepartmentService){
DepartmentService.getData({courthouse:"Some Court House"})
.then(function(filteredDepartments){
$scope.departments = filteredDepartments;
})
})

I typically store shared code and data access layers in a service/factory. I can then pass those services/factories to any controller that needs the underlying data either by instantiation or by singleton pattern depending on the nature of the model (does it change or have a state vs immutable). This pattern lends itself well to the dependency injection available in angular.

Related

Where do I put regular functions, Angularjs

I'm getting into Angularjs. I'm want to re-use a function, resetForm() function. My question is, do I still put that inside my controller $scope or create a factory or service?
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
//do I add my function here?
function name() {};
}]);
if it is a resetForm() function then I assume it is dealing with DOM. I would suggest you to declare this function inside your controller since you will need access to $scope to reset form fields (direct DOM access is strictly prohibited in AngularJS). You can refer to below sample code
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
var resetForm = function() {
// your logic to reset form with help of $scope
};
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
}]);
Note: You don't need to declare resetForm function as $scope.resetForm if you don't plan to call it from your template file.
If you want to re-use it across multiple controllers, a Factory or a Service is probably the best way to share it without duplication of code. You can then call on either one of these from all your controllers.
The added benefits to this pattern are that, not only do you save yourself from duplicating code, but you can also store variables and share those as well.
Both will work, but you can read some interesting discussion on Factory vs Service if you have trouble with which one to choose.
The things goes like this:
We will write functions in controllers if that function is normally manipulating model and is only relevant to that controller.
We write services normally for giving data to controllers such as from a asynchronous API call, and for sharing data in between controllers.
In your case, if you want a utility function you can use a service, but resetForm function is more like controller specific, because it's gonna clear some model values. In future you may want to add more conditions and operations in that function which may produce complex code, if you use a service for that.
If that function is a 'non-gonna change function' using a service is good way to go. (code re-usability and all), but otherwise, wrap all logic in one place is more good.
(write it in controller)

AngularJS - Correct way to share data between two controllers

I'd like some advice on how to share some data between two or more controllers in AngularJS.
For now I'm just dealing with two controllers, but in the future I will have more controllers that will also want to use this same data. Right now I have a navigation-controller which is controlling the side navigation and the header. And for ease of understanding, let's say the second controller is called content-controller which is responsible for dealing with all the content.
I want to dynamically load the content based on whatever the user searches for and the search bar is in the side navigation, so this searchTerm needs to be accessible by both controllers. In the future, I would also implement some other features which would probably need to access this searchTerm as well.
In terms of the HTML structure, the content-controller is inside the navigation-controller.
My first thought was to make searchTerm globally available by sticking it in $rootScope, but I'm unsure if this is an efficient/secure way to do it.
My second thought was to take the searching aspects and put them into a service. Inside this service I would put functions which would speak to the API in order to get the necessary data. This would mean on the search bar, I can make the submit search button access the service and run something like FooService.update(searchTerm).
What do you think the best way to deal with this scenario is?
Sharing data between controllers has always been a prominent requirement. You have a couple of options out there :
Factory
Services
You can refer to this answer, for more details upon the differences.
Using services is definitely the better option, since you won't be polluting the root scope with extra variables [That are destined to grow in numbers as your have already mentioned].
A possible way to store your data in services, and access them in controllers and HTML effortlessly can be described as :
Create a service, that will hold all the model variables.
angular.service("dataService", function() {
this.value1 = "";
this.value2 = "";
});
reference that service in your controllers, saving their reference in the scope.
angular.controller("myCntrl1", function($scope, dataService) {
$scope.dataService = dataService;
});
angular.controller("myCntrl2", function($scope, dataService) {
$scope.dataService = dataService;
});
Now in your html, you refer all your modal variables using the service reference :
// Controller 1 view
<div ng-controller="myCntrl1">
<input type="text" ng-model="dataService.value1" />
</div>
// Controller 2 view
<div ng-controller="myCntrl2">
The value entered by user is {{dataService.value1}}
</div>
First of all i don't know whether it's gonna work for you.
You can use local storage.
By using this the same data can be accessed in any controller
Here's an example how it worked for me.
app.controller("loginCtrl", function($scope, $window){
$scope.submit = function(){
$window.localStorage.setItem = ("username", $scope.username);
};
});
app.controller("homeCtrl", function($scope, $window){
$scope.logout = function(){
$window.localStorage.getItem = ("username");
};
});

How to structure angular factor/service that manipulates DOM

I'm building a growl like UI in angular. I'd like to expose it as a factory (or service) to make it available in my controllers. Calling growl.add will result in a change in the DOM, so it seems like I should have a directive take care of that, rather than doing direct DOM manipulation in the factory. Assuming that a factory-directive combo is the best option (and please correct me if that is not a good assumption), the question is:
How best to communicate between the factory and the directive?
Specifically, how best to send messages from the factory to the directive? Other questions have well covered sending information the other way, with onetime callback.
See below the working example. I suspect there is a better way though..
For reference, I have played with other options:
A) have the directive watch the service, e.g.
$scope.$watch(function(){
growl.someFunctionThatGetsNewData()},
function(newValue){
//update scope
})
But this means that someFunctionThatGetsNewData gets called in every digest cycle, which seem wasteful, since we know that the data only gets changed on growl.add
B) send an 'event', either via routescope, or via event bindings on the dom/window. Seem un-angular
Since neither of those options seem good, I'm using the one below, but it still feels hacky. The register function means that the directive and the factory are tightly coupled. But then again from usage perspective they are tightly bound - one is no good w/o the other.
It seem like the ideal solution would involve declaring a factory (or service) that includes the directive in its declaration (and perhaps functional scope) so that it exposes a single public interface. It seems icky to have two separate publicly declared components that entirely depend on each other, and which have tight coupling in the interfaces.
Working example - but there must be a better way..
vpModule.directive('vpGrowl',['$timeout', 'growl', function ($timeout, growl) {
return {
template: '<div>[[msg]]</div.',
link: function($scope, elm, attrs) {
growl.register(function(){
$scope.msg = growl.msg;
});
$scope.msg = growl.msg;
}
};
}]);
vpModule.factory('growl', ['$rootScope', '$sce', function($rootScope, $sce) {
var growl = {};
growl.msg = '';
var updateCallback = function(){};
growl.add = function(msg){
growl.msg = msg;
updateCallback();
};
growl.register = function(callback){
updateCallback = callback;
};
return growl;
}]);
I would have your growl service decide what to show, not the directive. So, the service handles any timers, state, etc. to decide when to hide/show messages. The service then exposes a collection of messages which the directive simply binds to.
The directive can inject the service and simply place it in scope, and then bind an ng-repeat to the service's collection. Yes, this does involve a watch, but you really don't need to worry about the performance of a single watch like this.
link: function(scope, elm, attrs) {
scope.growl = growl; // where 'growl' is the injected service
}
and then in the directive template:
<div ng-repeat="msg in growl.messages">
...
</div>
I would implement following logic:
Service growl defines some property growlProp on $rootScope & update it on each call of growl.add
Directive set watcher on $rootScope.growlProp
So directive knows nothing about service & service knows nothing about directve.
And additional overhead related to watcher is minimum.

Create a model based on a Database entity

I'm new to AngularJS and I would like to understand how to properly separate the model from the controller. Till now I've always worked with the models inside the controllers. For instance:
angular.module("app").controller("customerController", ["Customer", "$scope", "$routeParams",
function (Customer, $scope, $routeParams){
$scope.customer = Customer.find({ID:$routeParams.ID});
}]);
This function retrieves a customer from the database and exposes that customer to the view. But I would like to go further: for example I could have the necessity to ecapsulate something or create some useful functions to abstract from the row data contained in the database. Something like:
customer.getName = function(){
//return customer_name + customer_surname
};
customer.save = function(){
//save the customer in the database after some modifies
};
I want to create a model for the Customer and reuse that model in lots of controllers. Maybe I could then create a List for the customers with methods to retrieve all customers from the database or something else.
In conclusion I would like to have a model that reflects a database entity (like the customer above) with properties and methods to interact with. And maybe a factory that creates a Customer or a list of Customers. How can I achieve a task like this in AngularJS? I would like to receive some advices for this issue from you. A simple example will be very useful or a theoretical answer that helps me to undestand the right method to approch issues like these in Angular. Thanks and good luck with your work.
Angular JS enables you to have automatic view updates when a model change or an event occur.
TAHTS IT!
it does so by using $watches which are a kind of Global Scope java script objects and stay in primary memory through out the life cycle of the angular js web app.
1.Please consider the size of data before putting anything onto the $scope because each data object you attach to it does +1 to $watch. As you are reading from a database you might have 100+ rows with >4 columns and trust me it will eat up client side processing.Pls do consider the size of your dataset and read about angular related performance issues for huge data set
2.to have models for your database entity i would suggest having plain javascript classes i.e. dont put everything on $scope (it will avoid un necessay watches! ) http://www.phpied.com/3-ways-to-define-a-javascript-class/
3.You wish to fire up events when the user changes the values. For this best i would suggest that if you are using ng-repeat to render the data in your array then use $index to get the row number where the change was done and pass this in ng-click i.e. and use actionIdentifier to distinguish in the kinds of events you want
ng-click="someFunc($index,actionIdentifier)"
You need to create a factory/service to do do the job, check jsfiddle
html:
<div ng-app="users-app">
<h2>Users</h2>
<div ng-view ></div>
<script type="text/ng-template" id="list.html">
<p>Users: {{(user || {}).name || 'not created'}}</p>
<button ng-click='getUser()'>Get</button>
<button ng-click='saveUser(user)'>Save</button>
</script>
</div>
js:
angular.module('users-app', ['ngRoute'])
.factory('Users', function() {
function User (user) {
angular.extend(this, user);
}
User.prototype.save = function () {
alert("saved " + this.name);
}
return {
get: function() {
return new User({name:'newUser'});
}
}
})
.config(function($routeProvider) {
$routeProvider
.when('/', {controller:'ListCtrl',templateUrl:'list.html'});
})
.controller('ListCtrl', function($scope, Users) {
$scope.getUser = function() {
$scope.user = Users.get();
}
$scope.saveUser = function(u) {
u.save();
}
})
Hope that help,
Ron

Angular JS - How to safely retain global value "extracted" using a service

I need an object to be globally accessible all throughout my Angular application, and I've gladly put this object in a value service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
ATM, I've acheived my goal with something like this:
global service code
app.service('myService', function() {
this.foo = function() {
// some code that returns an object
return computedObject
}
})
app.run(function ($rootScope, myService) {
$rootScope.global = {dbObject: myService.foo()}
})
And which I can use in any controller that pleases me by simply injecting $rootScope in it
However, I don't like the need of injecting the whole $rootScope wherever I need that damn object, and I trust is not much safe (or efficient?) either, since the team specifies
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
Do you, perchance, happens to know any way I can inject a service into a service value?
Or maybe any other Angular best practice which I could exploit?
I forgot one very important notice
The computation of the object could be quite computational intense, so I absolutely don't want it to be recomputed everytime I move from page to page, or anything else really.
Ideally Using $rootScope for storing a few globally required values is totally ok. But if you are still adamant on using a module, I suggest you use a provider.
Make your 'myService' a provider and that will let you configure the variables in the service.
More info here
AngularJS: Service vs provider vs factory
You could use $broadcast to send the value from the value service to myService.
You would still need to inject $rootscope into each of the services, but from then on you could just inject myService around the rest of the app.
Reference here.
I need an object to be globally accessible all throughout my Angular application
I would use service. Since Service is singleton you can register the service to all your controllers and share any data over service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
Just create one main service (Parent) and child service that will inherit the parent. (like abstract service in Java world).
Application.factory('AbstractObject', [function () {
var AbstractObject = Class.extend({
virtualMethod: function() {
alert("Hello world");
},
abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable
throw new Error("Pure abstract call");
}
});
return AbstractObject; // You return class definition instead of it's instance
}]);
Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) {
var DerivedObject = AbstractObject.extend({
virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world`
alert("Hey!");
this._super();
},
abstractMethod: function() {
alert("Now I'm not abstract");
}
});
return DerivedObject;
}]);
Now, we can add some logic into AbstractObject and continue use DerivedObject
Here is example Plunker

Categories

Resources