Angular js - echoing data across multiple html pages - angularjs

I've worked with many languages and environments (predominately iOS & C#) and have always managed to avoid working with scripting languages. That avoidance streak has come to an abrupt end: as a huge angularjs project was thrown in my lap - now I'm scrambling to understand this very strange world. Some features are really cool, other techniques have me thoroughly baffled. I've spent weeks reading tutorials, studying examples and I still cannot solve a relatively simple problem regarding best practices and structure of the code.
This is what I need to do: I have a form, where the user will input data (for argument's sake, its two fields of number type.) I need to have a banner at the top of the page with the sum of the two input fields - that by itself is relatively easy - but the problem for me, is repeating this banner on subsequent pages.
Home page will contain links to:
page 1
page 2
The link to page 2 will not be available until the user inputs data on page 1, forcing the user to visit page 1, first. The banner element needs to be a separate file. Page 2 is a passive display of the data, Page 1 is only page that can actively edit the data.
page 1: would look like this --
banner.html (sum of fields A & field B)
input field A
input field B
page 2:
banner.html (sum of field A & field B)
Lorem Ipsum ....
What's the best way to achieve this task?

You can have an index page with the banner on top, and partials using the same controller. The value of the banner will be a controller variable.
To use partials, inside the index page, you'll need to include the ngRoute module, and the script tag linking to it.
You'll have a div like this.
<div ng-view=""></div>
You'll have a partialRoutes.js file looking something like this.
myApp.config(function($routeProvider){
$routeProvider
.when('/',{
templateUrl: './partials/things.html'
})
.when('/stuff',{
templateUrl: './partials/stuff.html'
})
.when('/otherstuff',{
templateUrl: './partials/otherstuff.html'
})
.otherwise({
redirectTo: '/',
})
});
When you include ngRoute it will look something like this.
var myApp = angular.module('myApp', ['ngRoute']);
Here are the docs for ngRoute. Hope I helped.
https://docs.angularjs.org/api/ngRoute

Personally, I would have a service, lets call it userData. Your inputController would then write the details of the inputs to the userData service.
Your bannerController would then be notified of changes to the data in the userData service (via $watch or $broadcast)
This way when you switch pages the bannerController should not change and will still display this information
Notes:
This relies on you using some kind of AngularJS routing technique such as NGroute or UI Router. If a hard page navigation is made then the userData will have to be stored server side.
It would probably be better for the banner to stay outside any ui-view so that it is unaffected by navigation, but if it is then as the userData service will still be alive and holding the correct data when it is recreated it will have access to the same data

If both pages have same controller, then $scope can be used to achieve this. If pages have different controller, $rootScope can be used to share variables.

Related

How to deal with templates of completely different areas (but same website) with AngularJS

I have a webapp with a main index.html that shows each page through an ng-view and a routeProvider.
Now I'm adding an entry page for users who are still not logged in. This page could also have his own ng-view to slide different sections for non logged users (welcome, what is, sign in, sign up ecc...) and his own head with css scripts ecc...
I'd rather that both the webapp index and the entry page index address to www.example.com (so nothing like www.example.com/welcome ).
I thought about a structure similar to this:
webapp/
main/
page1/
page2/
welcome/
section1/
section2/
index.html
welcome.js (angular module for the entry page)
.htaccess
index.html
webapp.js (angular module containing the routeProvider for the webapp)
Currently I have that .htaccess that rewrites all the fake queries to index.html so that everything is handled here.
So in particular:
1) How can I say to the routerProvider of webapp.js to go to /welcome/index.html when the user is not logged in, if I already use this condition for the webapp main page? (Should I use the run method? a brief example will be helpful)
.config(['$routeProvider', function($routeProvider){
$routeProvider
.when(...)
.when('/', {
templateUrl : 'main/webapp-main.html'
})
.when(...)
2) How do I prevent that everything in welcome/index.html will be loaded within the index.html ng-view? (maybe using ng-if to hide ng-view? or there is some way to control this behavour with the routerProvider?)
3) There is a better and simpler way to achieve all of this?
Thanks.
Update:
Ok I think there was an underlying problem in my question... I can't use a complete different page since everything is loaded in and switching to another page will cause the reload of the app loosing all the data.
So I suppose I should use the same main template for the entire website.
Then I add: there is a way in angularjs to load different style sheets dynamically?
this questions is old but i think is valid and can clarify someone a few thinks.
First of all you are suppose to use a single page for AngularJS apps, otherwise you will have to do something like a redirect link, and in your other-index.html load an entirely different Angular Application also loading every script needed in the first one.
1) Use ui-router is easy to work, and very robust, the only thing you need to have is a container for your ui-view then the ui-router will fill the container with the corresponding html
2) To prevent navigation you should do something like this:
angular.module('myApp', [])
.run(function($rootScope, mySessionService){
$rootScope.$on('$stateChangeStart',
function(event, next, nextParams, prev, prevParams) {
// The info of your currentUser could be obtain from a service
// or localStorage or cookies anyway you are already doing it
var authorized= checkAuthorization(next.authorazedRoles,
mySessionService.getCurrentRol());
if(!authorized) {
$rootScope.$state.go('defaultState');
// Or $rootScope.$emit('NOT_AUTHENTICATED');
// and some service will have to implement a listener for this emit
// $rootScope.$on('NOT_AUTHENTICATED', func..redirect to X State)
}
});
});

AngularJS register controller once

That's what I'm doing. There is application with pages and different controls that may be put on pages by site admin/editor. All pages share one ng-app defined on master page. All controls are supplied with .js files with angular controllers. Let's suppose that I have an image gallery block:
<div ng-controller='imageGalleryCtrl'>
do something amazing here
</div>
<script src='imageGallery.js'></script>
Inside script there is a simple controller registration like:
angular.module('myApp').controller('imageGalleryCtrl', ... );
So. If I have 10 image galleries, I'll execute controller registration 10 times. It looks like this will work, but hell - I don't want it to be so =)
For now I just have all controls' scripts registration on a master page, but I don't like it as well, because if there is no image gallery on a page, I don't want it's script be downloaded during page load.
The question is - is there any proper way to understand if controller have been registered in a module already and thus prevent it from re-registering?
---------------
Well, though I've found no perfect solution, I must admit that the whole idea isn't very good and I won't think about it before my site will grow too big to assemble whole angular app on master page.
You should declare your controller but once. Instead of having one controller per gallery, have your single controller handle all image galleries. The controller should make a request to the REST backend to fetch the images of the desired gallery.
I see that instead of ng-view, you're using the ng-controller directive, indicating that probably you're not using Angular's routing. Try switching to using routes.
Have a look at Angular.js routing tutorial. It shows you how to use the ngRoute module. Then, in the next chapter, the use of $routeParams is described. Via the $routeParams service, you can easily say which gallery should be displayed by providing its ID in the URL; only one controller will be necessary for all your galleries.
If you really must check whether a given controller has been declared, you can iterate through the already declared controllers (and services... and pretty much everything else) by checking the array angular.module("myApp")._invokeQueue. The code would probably look something like this (not tested!):
var isRegistered = function(controllerName)
{
var i, j, queue = angular.module("myApp")._invokeQueue;
for (i = 0, j = queue.length; i < j; ++i) {
if (
queue[i][0] === "$controllerProvider"
&& queue[i][1] === "register"
&& queue[i][2][0] === controllerName
) {
return true;
}
}
return false;
};
Bear in mind however that while this may (or may not) work, it's far from being the correct thing to do. It's touching Angular's internal data that's not meant to be used in your code.

Do we need multiple controllers to implement routes in angularjs?

There is chance that I might not be able to explain my problem properly. Let me try.
I am developing a single page application using angular. This app basically displays the episodes of an online novel series. There is a navigation bar, which has query menus (Like, latest episode, episode of a particular date, episodes with a particular tag, etc). For each of these queries, i want a separate url.
/latest - should display the latest episode
/tag/:tagname - should return all episodes with that tag.
For all these queries, the resultant view is the same (list of episodes). So I will be using the same partial for all routes.
My question is, Should I actually create a new controller for each query? like, LatestEpisodeController, TagController?
Is there anyway I can use the url to determine what the user wants and run that query from within the same controller?
Ofcourse you can use same controller in routing definition, the question is what is the purpose of that? It will be worse to debug it later, if you have a shared functionality it's better to turn it into a factory or service and then use in controllers.
But the answer is YES, you can use same controllers and implement different behaviour basing on i.e. $location.path()
yes you can use single controller for multiple routing..
you can create different functions in controller and in each function do the according job.
In my case I have created different html page for different url and registered same controller for the html pages and in the html page I have called controller method using ng-init in div portion.
You can use same controller and same views as you wish...
$location can help you to get current path or full url if you want and you can call your service depends on your path...
here I write a little example for you to get the idea
PLUNKER

Updating URL in Angular JS without re-rendering view

I'm building a dashboard system in AngularJS and I'm running into an issue with setting the url via $location.path
In our dashboard, we have a bunch of widgets. Each shows a larger maximized view when you click on it. We are trying to setup deep linking to allow users to link to a dashboard with a widget maximized.
Currently, we have 2 routes that look like /dashboard/:dashboardId and /dashboard/:dashboardId/:maximizedWidgetId
When a user maximizes a widget, we update the url using $location.path, but this is causing the view to re-render. Since we have all of the data, we don't want to reload the whole view, we just want to update the URL. Is there a way to set the url without causing the view to re-render?
HTML5Mode is set to true.
In fact, a view will be rendered everytime you change a url. Thats how $routeProvider works in Angular but you can pass maximizeWidgetId as a querystring which does not re-render a view.
App.config(function($routeProvider) {
$routeProvider.when('/dashboard/:dashboardId', {reloadOnSearch: false});
});
When you click a widget to maximize:
Maximum This Widget
or
$location.search('maximizeWidgetId', 1);
The URL in addressbar would change to http://app.com/dashboard/1?maximizeWidgetId=1
You can even watch when search changes in the URL (from one widget to another)
$scope.$on('$routeUpdate', function(scope, next, current) {
// Minimize the current widget and maximize the new one
});
You can set the reloadOnSearch property of $routeProvider to false.
Possible duplicate question : Can you change a path without reloading the controller in AngularJS?
Regards
For those who need change full path() without controllers reload
Here is plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
I realize this is an old question, but since it took me a good day and a half to find the answer, so here goes.
You do not need to convert your path into query strings if you use angular-ui-router.
Currently, due to what may be considered as a bug, setting reloadOnSearch: false on a state will result in being able to change the route without reloading the view. The GitHub user lmessinger was even kind enough to provide a demo of it. You can find the link from his comment linked above.
Basically all you need to do is:
Use ui-router instead of ngRoute
In your states, declare the ones you wish with reloadOnSearch: false
In my app, I have an category listing view, from which you can get to another category using a state like this:
$stateProvider.state('articles.list', {
url: '{categorySlug}',
templateUrl: 'partials/article-list.html',
controller: 'ArticleListCtrl',
reloadOnSearch: false
});
That's it. Hope this helps!
We're using Angular UI Router instead of built-in routes for a similar scenario. It doesn't seem to re-instantiate the controller and re-render the entire view.
How I've implemented it:
(my solution mostly for cases when you need to change whole route, not sub-parts)
I have page with menu (menuPage) and data should not be cleaned on navigation (there is a lot of inputs on each page and user will be very very unhappy if data will disappear accidentally).
turn off $routeProvider
in mainPage controller add two divs with custom directive attribute - each directive contains only 'templateUrl' and 'scope: true'
<div ng-show="tab=='tab_name'" data-tab_name-page></div>
mainPage controller contains lines to simulate routing:
if (!$scope.tab && $location.path()) {
$scope.tab = $location.path().substr(1);
}
$scope.setTab = function(tab) {
$scope.tab = tab;
$location.path('/'+tab);
};
That's all. Little bit ugly to have separate directive for each page, but usage of dynamic templateUrl (as function) in directive provokes re-rendering of page (and loosing data of inputs).
If I understood your question right, you want to,
Maximize the widget when the user is on /dashboard/:dashboardId and he maximizes the widget.
You want the user to have the ability to come back to /dashboard/:dashboardId/:maximizedWidgetId and still see the widget maximized.
You can configure only the first route in the routerConfig and use RouteParams to identify if the maximized widget is passed in the params in the controller of this configured route and maximize the one passed as the param. If the user is maximizing it the first time, share the url to this maximized view with the maximizedWidgetId on the UI.
As long as you use $location(which is just a wrapper over native location object) to update the path it will refresh the view.
I have an idea to use
window.history.replaceState('Object', 'Title', '/new-url');
If you do this and a digest cycle happens it will completely mangle things up. However if you set it back to the correct url that angular expects it's ok. So in theory you could store the correct url that angular expects and reset it just before you know a digest fires.
I've not tested this though.
Below code will let you change url without redirection such as: http://localhost/#/691?foo?bar?blabla
for(var i=0;i<=1000;i++) $routeProvider.when('/'+i, {templateUrl: "tabPages/"+i+".html",reloadOnSearch: false});
But when you change to http://localhost/#/692, you will be redirected.

Angularjs - using $routing - when I manually put "#/section/ + myId" , it resets my page

So I am trying to implement routing in my app. Here is a sample jsFiddle of what I am trying to do: http://jsfiddle.net/GeorgiAngelov/9yN3Z/114/
So far everything is working fine and as I am moving through the sections, my routing changes and everything is fine.
The problem is the following: when I manually type the section id in my url bar like this my local host : /section/s-3 I get redirected to s-1 and my page refreshes and I loose all of the sections and roots I have added so far. Sometimes it works, and I can switch between sections like that, but sometimes it refreshes, and it happens when I click enter twice.
It's an odd problem and I am not certain on how can I debug it. This is my actuall routing service.
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
redirectTo: '/section/1',
templateUrl: '/tpl.html',
})
.when('/section/:sectionId', {
templateUrl: '/tpl.html',
})
.otherwise({
redirectTo: '/'
});
});
I tested your app on chrome and on firefox. Using chrome, if you select the url and type enter (without changing it) it reloads the page, so hitting enter twice will always refresh the page. Using firefox, this does not happen, so apparently you don't have this problem on FF (although the user can press the refresh button directly).
If you refresh the page, you obviously lose what you have inserted. You need to persist the information you want to preserve between refreshes. If you don't want to send this to your server, an alternative is to use html5 local storage. You can see an example here:
http://todomvc.com/architecture-examples/angularjs/#/
I'we watched your application and from my point of view you're using an odd way to add DOM object to your view, kinda like jquery templating.
Actually each times you click inside a section you're using a different scope and adding a root in a scope it's not visible to the others: instead you should use a service shared between the controllers, this way only the service contains all the sections and all the roots: angularjs services
Later I'will update your jsfiddle, hope this helps for now.

Resources