AngularJS - make variable accessible to whole app - javascript

Below is a piece of code that connects with a Firebase database.
I have got a value for numberOfUsers and now I would like to use this variable in the html like so {{numberOfUsers}}.
I'm not really sure the best way to do this or if I need to use controllers also? Sorry if it's a stupid question, I'm still learning Javascript and Angular.
angular.module('myApp', ['ngRoute', 'firebase'])
var userList = new Firebase("https://my-app.firebaseio.com/presence/");
userList.on("value", function(snap) {
numberOfUsers = snap.numChildren();
console.log("Users = " + numberOfUsers);
});
;
http://jsfiddle.net/HB7LU/11820/
Any help will be much appreciated.
Thanks

The formal way to make a value available would be to put it in $rootScope, but it might be better to expose it as part of a service.

Try using constant
http://jsfiddle.net/HB7LU/11818/
var myApp = angular.module('myApp',[]);
myApp.constant('numUsers', 4);
function MyCtrl($scope,numUsers) {
$scope.name = 'Superhero';
$scope.numUsers = numUsers;
$scope.addUser = function(){
numUsers++;
$scope.numUsers = numUsers;
}
}

You can use a constant to achieve the same as Lucas suggested it. However, instead of creating a constant service for every value you can group then together like this :
angular.module("myModule")
.constant("CONST", { "KEY1" : "VALUE1",
"KEY2" : "VALUE2"});
This way you can gather a bunch of constants together and use it like:
CONST.KEY1
CONST.KEY2
EDIT: Your problem seems to be very different.
First of all you should use the AngularJS flavour of Firebase. Its called AngularFire. You can find out more about it here. I will answer the question of rendering the UI based on Model changes. AngularJS promotes the MVC pattern. You need objects of Service, Controller and View (HTML page) to achieve the functionality you want.
In the example I am providing below, everything is clubbed into one file (index.html) but ideally, the code should be separated.
<div ng-app="myapp">
<div ng-controller="PostCtrl" >
<ul>
<li ng-repeat="post in posts"> <!-- $scope.posts of PostCtrl" -->
<div>
<span>{{post}}</span> <!-- {{}} is used to render data -->
</div>
</li>
</ul>
</div>
<script>
//factory is a type of service. Services are used to write business logic or fetch data from the backend. Your Firebase related calls should come here.
angular.module("myapp", []).factory("myService", function($http) {
return {
fetchData: function() {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; //can also be a request to the backend returning data.
}
};
});
//a controller connects a service to the HTML file. $scope service provided by AngularJS is used to achieve this.
angular.module("myapp").controller("PostCtrl", ["$scope", "myService", function($scope, myService) {
//posts variable is used in HTML code to retrieve this data.
$scope.posts = myService.fetchData();
}]);
</script>
</div>
To learn the basics of AngularJS you can go through codeschool tutorials. They are interactive and start from the basics.

Related

What is the right way to send data between modules in AngularJS?

One of the great things about angular is that you can have independent Modules that you can reuse in different places. Say that you have a module to paint, order, and do a lot of things with lists. Say that this module will be used all around your application. And finally, say that you want to populate it in different ways. Here is an example:
angular.module('list', []).controller('listController', ListController);
var app = angular.module('myapp', ['list']).controller('appController', AppController);
function AppController() {
this.name = "Misae";
this.fetch = function() {
console.log("feching");
//change ListController list
//do something else
}
}
function ListController() {
this.list = [1, 2, 3];
this.revert = function() {
this.list.reverse();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="app" ng-app="myapp" ng-controller="appController as App">
<div class="filters">
Name:
<input type="text" ng-model="App.name" />
<button ng-click="App.fetch()">Fetch</button>
</div>
<div class="list" ng-controller="listController as List">
<button ng-click="List.revert()">Revert</button>
<ul>
<li ng-repeat="item in List.list">{{item}}</li>
</ul>
</div>
</div>
Now, when you click on Fetch button, you'll send the name (and other filters and stuff) to an API using $http and so on. Then you get some data, including a list of items you want to paint. Then you want to send that list to the List module, to be painted.
It has to be this way because you'll be using the list module in diferent places and it will always paint a list and add some features like reordering and reversing it. While the filters and the API connection will change, your list behaviour will not, so there must be 2 different modules.
That said, what is the best way to send the data to the List module after fetching it? With a Service?
You should be using Angular components for this task.
You should create a module with a component that will be displaying lists and providing some actions that will modify the list and tell the parent to update the value.
var list = angular.module('listModule', []);
list.controller('listCtrl', function() {
this.reverse = function() {
this.items = [].concat(this.items).reverse();
this.onUpdate({ newValue: this.items });
};
});
list.component('list', {
bindings: {
items: '<',
onUpdate: '&'
},
controller: 'listCtrl',
template: '<button ng-click="$ctrl.reverse()">Revert</button><ul><li ng-repeat="item in $ctrl.items">{{ item }}</li></ul>'
});
This way when you click on "Revert" list component will reverse the array and execute the function provided in on-update attribute of HTML element.
Then you can simply set your app to be dependent on this module
var app = angular.module('app', ['listModule']);
and use list component
<list data-items="list" data-on-update="updateList(newValue)"></list>
You can see the rest of the code in the example
It is very simple. Please take a look at this small snippet. comments are added to highlight.
You can have a common module that contains all the data that needs to be shared across modules by two steps
adding module dependency
injecting corresponding provider in the respective module's controller
angular.module('commonAppData', []).factory('AppData',function(){
var a,b,c;
a=1;
return{
a:a,
b:b,
c:c
}
})
angular.module('list', ['commonAppData']).controller('listController', ListController);
var app = angular.module('myapp', ['list','commonAppData']).controller('appController', AppController);
function AppController(AppData) {
//assigning a variable
AppData.a=100;
this.name = "Misae";
this.fetch = function() {
console.log("feching");
//change ListController list
//do something else
}
}
function ListController(AppData) {
//Using the data sent by App Controller
this.variableA=AppData.a;
this.list = [1, 2, 3];
this.revert = function() {
this.list.reverse();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="app" ng-app="myapp" ng-controller="appController as App">
<div class="filters">
Name:
<input type="text" ng-model="App.name" />
<button ng-click="App.fetch()">Fetch</button>
</div>
<div class="list" ng-controller="listController as List">
<b>Shared variable : {{List.variableA}}</b>
<br>
<button ng-click="List.revert()">Revert</button>
<ul>
<li ng-repeat="item in List.list">{{item}}</li>
</ul>
</div>
</div>
There are a few ways to handle objects acress multiple controllers. Here are two.
1. Using Angulars $rootScope
You can assign an object to $rootScopewhich will hold up all information. This object can be passed into every controller through Angulars dependency injection. Also you can listen up to changes on your object by watching it through $watch or $emit.
Using $rootScope is an easy way, but may lead to performance issues on larger applications.
2. Using services
Angular provides a possibility to share object through services. Instead of defining your object inside of your controller, you could also do that inside of a service. Doing so you could inject that service into any controller and use it's values across your application.
function AppController(listService) {
// reference to the injected data
}
function ListController(listService) {
// update data
}
There are many ways to pass data from one module to another module and many ppl have suggested different ways.
One of the finest way and cleaner approach is using a factory instead of polluting $rootScope or using $emit or $broadcast or $controller.If you want to know more about how to use all of this visit this blog on
Accessing functions of one controller from another in angular js
By simply inject the factory you have created in main module and add child modules as dependancy in main module, then inject factory in child modules to access the factory objects.
Here is a sample example on how to use factory for sharing data across the application.
Lets create a factory which can be used in entire application across all controllers to store data and access them.
Advantages with factory is you can create objects in it and intialise them any where in the controllers or we can set the defult values by intialising them in the factory itself.
Factory
app.factory('SharedData',['$http','$rootScope',function($http,$rootScope){
var SharedData = {}; // create factory object...
SharedData.appName ='My App';
return SharedData;
}]);
Service
app.service('Auth', ['$http', '$q', 'SharedData', function($http, $q,SharedData) {
this.getUser = function() {
return $http.get('user.json')
.then(function(response) {
this.user = response.data;
SharedData.userData = this.user; // inject in the service and create a object in factory ob ject to store user data..
return response.data;
}, function(error) {
return $q.reject(error.data);
});
};
}]);
Controller
var app = angular.module("app", []);
app.controller("testController", ["$scope",'SharedData','Auth',
function($scope,SharedData,Auth) {
$scope.user ={};
// do a service call via service and check the shared data which is factory object ...
var user = Auth.getUser().then(function(res){
console.log(SharedData);
$scope.user = SharedData.userData;// assigning to scope.
});
}]);
In HTML
<body ng-app='app'>
<div class="media-list" ng-controller="testController">
<pre> {{user | json}}</pre>
</div>
</body>
It depends on the circumstance. Is there a parent child relationship? Is the relationship unknown, or you simply want to avoid having to worry about it at all?
I think this post lays it out well (it always seems to be helpful):
http://mean.expert/2016/05/21/angular-2-component-communication/
AngularJS provides $on, $emit, and $broadcast services for event-based communication between controllers.
So, if we want to pass data from the inner controller (listController) to outer controller (appController) then we have to use $emit. It dispatches an event name upwards through the scope hierarchy and notify to the registered $rootScope.
Working Plunker : https://plnkr.co/edit/szf9jHvvOPLOvQc5sQI2?p=preview
This plunker is not as per the exact requirement as I don't know the API response but this sample plunker describe the problem statement.
I think you can use the publisher-subscriber pattern implemented in $rootScope.
In the ListController you have to inject $rootScope and after that you have to subscribe to an arbitrary called event that could have the pattern _data_received
$rootScope.$on('ingredients_data_received', function(ingredients) { prepare_recipe();});
so in your AppController you have to call $rootScope.$emit once the data of that list has been received
$rootScope.$emit('ingredients_data_received', ingredients);
This is just a way to pass data, you can also push those data or the promise in a $rootScope property but this is not a good practice, or you can create your own Service that manage the data for you (remember that you are working with a frontend framework so the controller has to control the view, the business logic has to be transfered to a service, not to a crontroller).
I like to use angular resource with a caching layer to persist data to multiple controllers using a singular method. This has a few benefits namely any controller has the same entry point to data. Keep in mind sometimes you need to fetch fresh data when navigating from one portion of the site to another so persisting http data is not always ideal.
'use strict';
(function() {
angular.module('customModule')
.service('BookService', BookService);
BookService.$inject = ['$resource'];
function BookService($resource) {
var BookResource = $resource('/books', {id: '#id'}, {
getBooks: {
method: 'GET',
cache: true
}
});
return {
getBooks: getBooks
};
function getBooks(params) {
if (!params) {
params = {};
}
return BookResource.getBooks(params).$promise;
}
}
})();
In any controller
BookService.getBooks().then(function(books) {
//books will be cached so invoking the call will return the same set of data anywhere
});
Services in angular are used to share functionality between components. Please try to keep them simple and with a single responsibility/purpose.
An idea would be creating a store service that will cache every response from the api in your application. Then you don't have to request it every time.
Hope it helps! ;)

angular.js get links from database

I'm using laravel 5 for backend and angular.js for frontend. App is completely driven by ajax requests.
I'm showing some static links(which would be always on any page visible) in my view like this:
<li ng-repeat="link in links">
{{link.name}}
</li>
What's the best way to handle this? My view doesn't contain .blade(because of angular) in name so I'm not able to load these links throw php.
Should I try to make some php workaround or load it in angular throw $http?
If in angular how should I get it into this config function?
app.config(function($routeProvider, $locationProvider) {
var links = ??; // http doesn't want to work here
links.map(function(item) {
$routeProvider.when('/'+item.url, {
..
});
});
});
I would suggest first to integrate the AngularJS to work together with blade template engine in Laravel. This can be done easy with angular service called interpolation.
Here is how you will make it:
<script>
var customInterpolationApp = angular.module('customInterpolationApp', []);
customInterpolationApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
customInterpolationApp.controller('DemoController', function() {
this.label = "This binding is brought you by [[ ]] interpolation symbols.";
});
</script>
<div ng-app="App" ng-controller="DemoController as demo">
[[demo.label]]
</div>
Here is angularjs page explaning the $interpolateProvider
This way you will not have conflict between AngularJS and Blade Template Engine
You should load it with AngularJS
You should attach the result to a $scope.links in a custom AngularJS controller. Based on the code from step 1 you can inject $http into anonymous DemoController function, and after this inside controller do something like this:
var self = this;
$http.get('YOUR API LINK', function (data) {
self.links = data;
});

Originzational MVC and Angular keeping from injecting what I don't need

My problem is I have one ng-app. Does that mean I have to do dependency injection for plugins I may not be using on that given view? Example I bring in ngTagsInput does that mean I have to do it even when the view doesn't call for it? That would mean I have to include that js for every view even if it doesn't use ngTagsInput.
I have a very large MVC .NET application and I am trying to figure out what is he best way to handle bringing in external plugins.
I have some code like so in our Main _Layout template:
<html ng-app="ourApp">
<head>
<!-- all of our includes are here -->
</head>
<body>
<directive></directive>
<anotherdirective></anotherdirective>
#RenderBody()
</body>
</html>
RenderBody is where MVC slides in our views from our mvc routing.That view may look like so:
<script src="~/Scripts/HomeAngular.js"></script>
<div ng-controller="HomeCtrl">
<directive3></directive3>
</div>
App JS:
var app = angular.module('ourApp', ['ngTagsInput']);
IS there a way I can get around having to inject ngTagsInput on every view page even if i don't need it?
There are several different ways to handle Dependency Injection (DI) in angular. In this first example, you simply define ngTagsInput before declaring the controller. If ngTagsInput is a service, for example, you'll need to return an object with methods that allow you to access the data inside of that service.
For example:
app.service('ngTagsInput', function($scope) {
var data = {};
return {
getData = function() {
return data;
},
setData = function(val) {
data = val;
}
};
});
app.controller('HomeCtrl', function($scope, ngTagsInput) {
// ...
$scope.tags = ngTagsInput; // whatever you want to do with it
});
However, there's a problem...
In the example above, if you run that code through a minifier, you'll get $scope turned into some variable (a, b, x, etc...) and angular wont know what to do with that.
In this method, we use an array. The last item in your array is your lambda function for the controller, which takes 1 argument for each previous item in the array:
app.controller('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
This code can be run through a minifier just fine, since the dependencies are strings in the array and can be renamed to anything you want inside the function's parameters.
DI also works in directives, factories, filters, and services with the same syntax; not just controllers and modules.
app.directive('HomeCtrl', ['$scope', 'ngTagsInput', function(scp, ngTagIn) {
// ...
}]);
Couldn't you break down your application further into smaller modules instead of just one. Then you can inject the smaller modules into the app module and only inject the ngTagsInput dependency on the modules that actually need it. That's how I typically break up my application by function or area.

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

Angularjs - dynamic pages, same template, how to access and load article by id

I am slightly puzzled on how to use angularjs to build a blog-like web site.
If you think of an ordinary blog,,, and say,, I am building it using php and mysql.. what I don't understand is how do I make angular get an article based on id..
I can load data for a list of all articles.. I can load data for a single page (from a static json),, I understand how to send http requests,, but how do I access
mysite.com/page?id=1 or
mysite.com/page?id=2 or
mysite.com/page?id=3
Obviously, I want to load the same template for each separate blog post.. but I have not yet seen a single example that explains this simply.
If I have three posts in my database with id 1,2,3 how is angular meant to generate links to each individual article? I understand that I can load data to assemble urls but what urls? I suppose I am confused with routing.
Could you recommend a SIMPLE and understandable example of this? Could you suggest how to think about this?
Thanks!
In this short explanation I will use examples based on official tutorial.
Propably somwhere in your application you created controllers module with code close to that:
var blogControllers = angular.module('blogControllers', []);
// other controllers etc.
blogControllers.controller('BlogPostCtrl', ['$scope',
// more and more of controller code
We will be back here in a moment :) If you have controllers module, you can create a route linked to the specific controller:
var blogApp = angular.module('blogApp', [
'ngRoute',
'blogControllers'
]);
blogApp .config(['$routeProvider',
function($routeProvider) {
$routeProvider.
// other routes
when('/post/:postId', {
templateUrl: 'partials/blog-post.html',
controller: 'BlogPostCtrl'
}).
// other...
}]);
Well but what we can do with this :postId parameter? Lets go back to our controller:
blogControllers.controller('BlogPostCtrl', ['$scope', '$routeParams', 'BlogPost',
function($scope, $routeParams, BlogPost) {
$scope.post = BlogPost.get({postId: $routeParams.postId});
}]);
As you see, we are passing $routeParams here and then our BlogPost resource (it will be explained). Simplifying (again), in $routeParams you have all the params that you put in the $routeProvider for exact route (so :postId in our example). We got the id, now the magic happens ;)
First you need to add services and/or factories to your app (and look, we are using ngResource):
var blogServices = angular.module('blogServices ', ['ngResource']);
blogServices.factory('BlogPost', ['$resource',
function($resource){
return $resource('action/to/get/:postId.json', {}, {
query: {method:'GET', params: { postId: 'all' }, isArray:true}
});
}]);
Now you know what is our BlogPost in controller :) As you see default value for postId is "all" and yes, our api should retrieve all posts for postId = "all". Of course you can do this in your own way and separate this to two factories, one for details and one for list/index.
How to print all of the blog names? Quite simple. Add list routing without any special params. You already know how to do this so let's skip it and continue with our list controller:
blogControllers.controller('BlogListCtrl', ['$scope', 'BlogPost',
function($scope, BlogPost) {
$scope.blogPosts = BlogPost.query(); // no params, so we are taking all posts
}]);
Voila! All posts in our $scope variable! Thanks to this, they are accessible in the template/view :) Now we just need to iterate those posts in our view, for example:
<ul class="blog-posts">
<li ng-repeat="blogPost in blogPosts">
{{blogPost.title}}
</li>
</ul>
And this is it:) I hope you will find AngularJS quite easy now!
Cheers!
I think what you want to use for this is $routeParams. Have a look at this video from egghead.io which explains how to use it:
http://egghead.io/lessons/angularjs-routeparams
It's a good practice to use hash navigation. So your routing should look like this
mysite.com/blog/#!/id=1
mysite.com/blog/#!/id=2

Categories

Resources