How do I share data across controllers in AngularJS? - angularjs

I am doing a project that uses AngularJS. I have created partial pages where each page has its own, different controller.
Site Structure
/ProjectRoot
/css
/js
angular.js
jquery.js
/partials
login.html
page1.html
index.html
index.html
<!DOCTYPE html>
<html ng-app="demoapp" >
<head>
scripts
</head>
<body ng-controller="maincontroller">
<ng-view></ng-view>
</body>
</html>
login.html
<div class="main-page" ng-controller="logincontroller">
--code---
</div>
So the full login page is rendered as:
<!DOCTYPE html>
<html ng-app="demoapp" >
<head>
scripts
</head>
<body ng-controller="maincontroller">
<div class="main-page" ng-controller="logincontroller">
--code---
</div>
</body>
</html>
I need to share data between the controllers. I tried using $scope.$parent.variablename but when I refresh page1.html it contains a blank value.
My questions are:
How do I create global variables which I can use throughout the project, that is with all partial pages and their controllers (perhaps $rootScope)?
Is the use of a cache is better option or not? (security concern)

To share state between controllers you should use an Angular service. Services are easy to define and use (see example below) and since they are singletons they are a great place to store shared state information. No need for caches or anything complex.
But then you also said:
I tried using $scope.$parent.variablename but when I refresh
page1.html it contains a blank value.
Refreshing an Angular app is always going to be a problem.
This is because your typical Angular app is a Single Page Application (SPA). Your state information persists only as long as the lifetime of the app, which for an SPA is the lifetime of the page. Thus, if you refresh the page then you restart the application and all your state information is lost.
To solve this problem you can use sessionStorage. This technology is well supported across browsers and allows you to persist your state data between page refreshes. This is especially important in Angular apps because they should always gracefully support the user refreshing the page (which includes clicking on the back and forward buttons).
You can combine all this into a simple service that persists its data into sessionStorage. This gives you state data that is shared between controllers and persisted between page refreshes.
Here is an example (in coffeescript) that stores a state datum called userToken:
Service
angular
.module 'app'
.service 'storageService', StorageService
class StorageService
setStorage:(key, value)->
json = if value is undefined then null else JSON.stringify value
sessionStorage.setItem key, json
getStorage:(key)->
JSON.parse sessionStorage.getItem key
userToken:(value=null)->
if value is null
#getStorage 'userToken'
else
#setStorage 'userToken', value
You can add as many data values to this service as you like, you can even store complex objects
Once you have defined a service you can use Angular's dependency injection to easily get hold of the service instance. Here are a couple of example controllers that show the storageService being injected and used:
Controller
angular
.module 'app'
.controller 'logincontroller', ($scope, storageService)->
$scope.setUserToken = (token)-> storageService.userToken(token)
.controller 'maincontroller', ($scope, storageService)->
$scope.userToken = storageService.userToken()

Related

Multiple pages share a master page

I'm working on an asp.net web api project, where Default.html calls certain web api endpoints.
Now I need to add another page, specifically another URL, say /About, to hold another kind of content.
I want /Default and /About share the same header and footer.
I have experience in asp.net webform, where I will write a master page like
<%# Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<asp:ContentPlaceHolder id="head" runat="server"/>
</head>
<body>
<div>Shared Header</div>
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server"/>
<div>Shared Fotter</div>
</body>
</html>
and each page is like
<%# Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
Hello world
</asp:Content>
Can I do similar in my current project? I want to keep server side as simple as possible. I don't like IIS Server Side Include because it requires IIS setup. I also want to avoid introducing server side nodejs. However, since I'm already using web api, I don't mind making more use of it.
I foresee all computations needed in my project can be done on client side, and currently they have been done on client side. Therefore if I introduce webform or MVC with empty server side logic, solely for the master page or layout, it seems not economic.
I'm inclined to use client-side solutions. Can AngularJS, ReactJS, VueJS, and sort of frameworks support this? Which one is easiest?
I heard HTML Import as well, but not sure how it will work.
It is difficult to answer your question because all of the frameworks you mentioned are capable of such "master paging". I use Vue.js in my everyday work so I can speak about that but it is only a personal preference.
In Vue.js you can use a "master page" with Vue-Router, you can read about it here:
https://router.vuejs.org/guide/#html
The main point is that you have to put a router-view tag into your "master page" and the replacement of the content will be managed by the router, based on the navigation (either programmatic by you or triggered by a user interaction).
I would suggest you use angularjs, what I am doing in one of my projects is basically I have one master .cshtml view in my project and this .cshtml includes header, footer, sidebar etc and to this .cshtml I reference all the angularjs controller and each angularjs controller has its own html.
The master view has its own angularjs controller as well.
Now lets suppose the user clicks on an option in the siderbar, lets say "invoice". Upon clicking on invoice, a function gets called in the angularjs controller of master view and that function simply changes the url to /invoice
In my app.js I am using $routeProvider which works like this:-
$routeProvider.
when('/invoice', {
templateUrl: 'Scripts/app/invoice/template/index.html'
})
so route provider basically changes the html according to the url and the html loaded in the browser has its own angularjs controller

Angular singe-page application routing within a page other than index.html

I have an application that is not a single-page application and different pages are rendered after being sent to the client from an API. After I navigate to a page, say example.html, with this method, is it possible to use a <div ng-view> in example.html and route between different views within example.html with $routeprovider. Or does the Angular routing have to happen within index.html?
How would I do so? What would the directory structure need to be?
Thanks!

Angularjs controller and view for subdomains

I have a website where users can reserve tickets for trains and buses. For each type of transport I use separate subdomain, also I use it for FAQ, Login page etc:
website.com
bus.website.com
train.website.com
login.website.com
faq.website.com
I don't want to put angularjs build to each of subfolder. Instead of this I would like to have each subdomin "virtually" and have only one main folder in /var/www directory
My website has simple structure:
<body>
<div class="header">...</div>
<div ng-view></div>
<div class="footer">...</div>
</body>
I want each subdomain has own controller and html template. I understand that it can't be achieved by $routeProvider.
So my question: can I have main controller which decides what current subdomain is through $location, and after this instantiate both specific controller (e.g. BusController) and template (e.g. BusTemplate.html) ?
Or I am sure there is better way to leverage angularjs for subdomains.

ui-router reinitializing angular controllers on state change

Is there an easy way using ui-router to force all my controllers to only be initialized once? Like a singleton. As of right now, every time I change to a state, the controller linked to that state is reinitialized. I don't want this to happen. This seems like it should be simple, but I could not find a solution to this anywhere online.
A common way to handle things like one-time or global operations is to have an application level controller on an element that wraps all your ui-views:
<html ng-app="app">
<head>...</head>
<body ng-controller="ApplicationController">
<div ui-view></div>
</body>
</html>
ApplicationController can then reponsible for one-time operations. It gets initialised once (when the app starts) and will persist between route and state changes.
The controllers associated with your states should only be concerned with constructing their own views, not performing one-time operations. If the state controllers need to access shared data, then that data should be stored in a service as suggested by ABOS. State controllers should request that shared data from the service, and the service should be the one deciding if it should return cached data, or make a fresh service call.

AngularJS Multi-Page App Site Boilerplate Site Structure Advice

I am looking for some guidance with creating an AngularJs multi-page app served by a Laravel Backend. All web app tutorials on the net point to creating SPAs and I am just getting started with Angular - so please go easy on me.
ProductPage example - http://example.com/products/widget
<html data-ng-app='ExampleApp'>
<head>
</head>
<body data-ng-controller='ProductController'>
// ProductPage Content Served by laravel with angular tags
<script type="text/javascript" src="/js/lib/angular.min.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript" src="/js/controllers/ProductController.js"></script>
</body>
</html>
CartPage Example - http://example.com/cart
<html>
<head>
</head>
<body data-ng-controller='CartController'>
// CartPage Content Served by web-server with angular tags
<script type="text/javascript" src="/js/lib/angular.min.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript" src="/js/controllers/CartController.js"></script>
</body>
</html>
So in the above examples, I have created two pages, which are served by the web server with pretty much all static content. But the pages have been marked up with angular tags. On each static page, I have referenced a different AngularJS controller.
Is this the right way of tackling the problem or should I be allowing app.js to load up the controllers / inject the dependencies?
Also, any guidance on sharing data between controllers in this multi-page app and links to decent resources / examples would be really helpful. e.g Would I need to pass e.g. Items added to shopping cart to an api from the product page, then query this api again to retrieve the cart contents?
You should include the ngRoute module and let AngularJS load the controllers for you. Please refer to AngularJS docs to find out how to work with routings.
As for sharing data between controllers, services are what you're looking for. Creating a service is as easy as this:
app.factory("ServiceName", [function() {
return {
somevar: "foo"
};
}]);
Then, in your controllers, you inject the service like this:
app.controller("ContactCtrl", ["$scope", "ServiceName", function($scope, svc) {
$scope.somevar = svc.somevar;
}]);
The service's state is retained for as long as you don't cause a physical page reload (which is why you should use ngRoute to load your controllers).
Here you can use ngroute directive with assigning controller name dynamically.
If we use ngroute , then ngroute $scope is parent scope for both pages(html views).
Form this scope you can easily pass data from one controller to other controller.
Probably the best boilerplate/template system that I have found is Yeoman. It has a large number of great Angular templates that you can use as a starting point, and also supports automatically creating models, views, etc. from subtemplates of the main template that you choose.
Take a look at the Yeoman Angular generator, it's one of the more well-maintained angular templates that will give you a good feel for the capabilities of using a Yeoman generator.
I've worked with ngSeed for the past two years now. When it got updated to use $state it felt like a decent way to do larger apps with Angular.
Things to remember:
modularize around functionals (not layers like most tutorials do),
keep your modules small and clean,
never use $rootScope,
encapsulate shared state in a service,
don't broadcast events, but watch state,
…
Check out fountainjs. They have good boilerplates for different UI technologies.
I put a basic angular multipage boilerplate on github. It covers a working example of ngRoute and the loading of html partials and images. There's also an angular button in one of the partials that logs a message to the console. Hope it helps
https://github.com/PrimeLens/angular-multipage-boilerplate
edit: there is a controller that encompasses all pages to hold data that you might want to pass from page to page.

Resources