Hold data between pages in angular js - angularjs

I am new to angular js.Please suggest which one is better . I have a page where I can select a no of check boxes and navigate to 2 nd page.I want to show the checked values when I press back button in browser.
Which one is better using $rootScope or stateparam or localstorage.Please consider performance also.

Going through your options one by one:
$rootScope: You should try to avoid using this option as it is associated to the global state. Use this option only if you want the data you set to be globally available throughout the app and do not want to reuse the same variable names anywhere else.
$stateParams: These typically go into the state URI as a dynamic part of the whole URL. This has only limited usage, and may not be a good idea for your use case where you need to store data from multiple checkboxes.
localStorage: This is slower than using a JavaScript variable, and hence not a good idea.
Services:
Here is a better solution for you, entirely within the AngularJS paradigm: AngularJS services
Excerpt from the link in point 3:
Data should also be stored in services, except where it is being bound
to the $scope. Services are singletons that persist throughout the
lifetime of the application, while controllers are transient between
application states. If data is stored in the controller then it will
need to be fetched from somewhere when it is reinstantiated. Even if
the data is stored in localStorage, it's an order of magnitude slower
to retrieve than from with a Javascript variable.
You can use the service to set and get your data inside your controller anywhere in the app in a consistent manner.
Here is an indicative example:
app.factory('formDetails', formDetails);
function formDetails() {
var formData = {};
return {
getProperty: function () {
return formData;
},
setProperty: function(values) {
formData = values;
}
};
};
Use it in your controller as:
app.controller('MyController', [ '$scope', 'formDetails' , function($scope, formDetails) {
$scope.checkboxData = {};
// on load call service 'get' function
$scope.checkboxData = formDetails.getProperty();
// on some event/watch function call service 'set' function
formDetails.setProperty($scope.checkboxData);
}]);

Related

How to access a dropdown in another controller scope in angularjs

I have a controller name is ParentController, which has a dropdown named ddlState. How to access this dropdown in another controller named CityController in Angular JS
use a factory.
Here is a sample for simple data exchange
app.factory('dataExchange', function() {
var ret = {
getData: getData,
setData: setData
};
var data = {};
return ret;
function getData() {
return data;
}
function setData(x) {
data = x;
}
});
Simply give your object to setData in your parent controller, and get it back in your child with getData
You have different ways to work with this,
1. It is always better to have the drop downs through a directive.then you can use the same directive for multiple drop down in multiple controllers by using $on function.
2. Else if you want to write and repeat the drop down code in all html and controllers, you can go with local storage system, So once call the API result and store in local storage (browser level), in other controllers also with out calling the API you can append the data into drop down from local storage.
Other way is keep the drop down values in a $rootScope variable,and you can use in where ever you want. (but this way is not recommended)

Passing data to new page using Onsenui

I am trying to call an API end point once a user clicks a button holding a myNavigator.pushPage() request. However,I can not get the $scope data generated from the $http.get request to be passed to the new page.
If I test using console.log('test'); inside the .success of the $http.get request I successfully get the log info in the console but any data held in $scope.var = 'something'; does not gets passed to the page! Really confused!
$scope.historyDetails = function(id){
var options = {
animation: 'slide',
onTransitionEnd: function() {
$http.get('http://xxx-env.us-east-1.elasticbeanstalk.com/apiget/testresult/testId/'+id).success(function(data) {
$scope.testscore = 'something'; // this is not getting passed to page!
console.log('bahh'); // But I see this in console
});
}
};
myNavigator.pushPage("activity.html", options);
}
Page:
<ons-page ng-controller="HistoryController">
...
<span style="font-size:1.2em">{{testscore}} </span><span style="font-size:0.5em;color:#555"></span>
...
</ons-page>
Yes, that's so because both pages has different controllers, resulting in different scopes. One can not access variables from one scope to another.
Hence one solution in this case can be using rootScope service.
Root Scope is parent scope for all scopes in your angular application.
Hence you can access variable of root scopes from any other scope, provided that you are injecting $rootScope service in that controller.
to know more about rootScope check this link.
Good luck.
Update 1:
check these articles
http://www.dotnet-tricks.com/Tutorial/angularjs/UVDE100914-Understanding-AngularJS-$rootScope-and-$scope.html
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
As Yogesh said the reason you're not getting your values is because if you look at $scope.testscore and try to find where is the $scope defined you will see that it's an argument for the controller function (thus it's only for that controller).
However we can see that the controller is attached to the page and you are pushing another page.
So in that case you have several options:
Use the $rootScope service as Yogesh suggested (in that case accept his answer).
Create your own service/factory/etc doing something similar to $rootScope.
(function(){
var historyData = {};
myApp.factory('historyData', function() {
return historyData;
});
})();
Technically you could probably make it more meaningful, but maybe these things are better described in some angular guides.
If you have multiple components sharing the same data then maybe you could just define your controller on a level higher - for example the ons-navigator - that way it will include all the pages. That would be ok only if your app is really small though - it's not recommended for large apps.
If this data is required only in activity.html you could just get it in that page's controller. For example:
myApp.controller('activityController', function($scope, $http) {
$http.get(...).success(function(data) {
$scope.data = data;
});
}
But I guess you would still need to get some id. Anyway it's probably better if you do the request here, now you just need the id, not the data.
You could actually cheat it with the var directive. If you give the activity page <ons-page var="myActivityPage"> then you will be able to access it through the myActivityPage variable.
And the thing you've been searching for - when you do
myNavigator.pushPage("activity.html", options);
actually the options is saved inside the ons-page of activity.html.
So you can do
myNavigator.pushPage("activity.html", {data: {id: 33}, animation: 'slide'});
And in the other controller your id will be myActivityPage.options.data.id.
If you still insist on passing all the data instead of an id - here's a simple example. In the newer versions of the 2.0 beta (I think since beta 6 or 7) all methods pushPage, popPage etc return a promise - which resolve to the ons-page, making things easier.
$scope.historyDetails = function(id){
myNavigator.pushPage("activity.html", {animation: 'slide'}).then(function(page) {
$http.get('...' + id).success(function(data) {
page.options.data = data;
});
});
});
Side note: You may want to close the question which you posted 5 days ago, as it's a duplicate of this one (I must've missed it at that time).

How to go back to the previous view with parameter in ionic?

In a view I have a link to choose a start location like below:
<input type="text" ng-model="placeStart" placeholder="place to start">
<input type="text" ng-model="weight" placeholder="goods weight">
and in the location page, I do choose a place, however, When I use $ionicHistory.goBack(), I could not pass the "place" back to the previous view. I also do not want to use state.go('previous view') to pass the "place", because in that way, I will lost the other input information in the previous view.
Here is :
$ionicHistory.backView().stateId;
don't ignore to include $ionicHistory on controller
There are three options which immediately come to mind. $rootScope, localStorage and using routing not goBack().
If you need the value from one view in another, and they're completely separate controllers etc then you need a way to pass them around.
You could create and then put the value into $rootScope.globals or similar.
You could store the value to localStorage before sending the user back.
You could redirect correctly to a route which allows the values to be included in the url and still show the provious page. For example the same route with and without values set, using - or 0 for not set depending on data type:
/an/example/route/-/-/
/an/example/route/0/0/
/an/example/route/123/456
Update:
There is actually a fourth way where you can send data between controllers using $broadcast and $on. The broadcast happens in the sending controller and the $on listens in the receiving controller(s) so you can send an update to values / an object etc. $on and $broadcast in angular
It depends on your situation but if you don't care to preserve state after a page reload which localStorage would be good for, but rather have the state be remembered just when you're going back (and not necessarily when you later navigate forward back into the view again) then I do this: I make a separate service, just an object that sticks around in which I can inject anywhere and store some variables. In Angular 1 this would be a service object.
angular.module('foo').factory("placeHelper", [
() => {
let _place = null;
class PlaceHelper {
set place(place){
_place = place;
}
get place(){
return _place;
}
reset(){
_place = null;
}
}
let helper = new PlaceHelper();
return helper;
}
]);
Then in your controller that you're going back to, you inject placeHelper and ask it for the place and restore your state via $scope.place = placeHelper.place and when in the UI for that controller someone selects a place, you just store it in the service, placeHelper.place = $scope.place.
I would use localStorage within the service if I wanted to keep the state around after a page refresh.
I don't like polluting $rootScope because it's harder to keep track after you start to have more than a few unrelated methods in there and your properties need to have longer names (or get grouped in objects anyway). It's better for maintainability to encapsulate and separate concerns.
Service variation:
The service could be an object literal instead of a class and you could directly set the properties instead of using methods if you wanted it to be a bit more simple.
angular.module('foo').factory("placeHelper", [
() => {
let helper = {
place: null,
reset(){
this.place = null;
}
};
return helper;
}
]);
I stumbled on the same problem recently. I ended up deciding to use $ionicHistory.currentView() and $ionicHistory.backView() (see documentation here). The former function returns an object associated to the current view, while the latter returns an object associated to the view you will go to after you call $ionicHistory.goBack().
Before calling $ionicHistory.goBack() on your location page you call $ionicHistory.backView() and define a new property to the returned object whose contents is the data you want to propagate to the other view.
On your other view, you change its '$ionicView.enter' event handler so it calls $ionicHistory.currentView(), which retrieves the object with the data you want.

How to config a angular.js provider dynamically?

The use case is that I have a custom service which needs to be configured based on user input.
So I created a service provider for that service, but now I can only config the provider inside the module.config call, which I think it is loaded only once during the life of the app.
Any solution for this?
Have your service provide some sort of a configuration API to set these configuration values as needed. As a simple example you might do something like this:
function myController(myService, $scope) {
$scope.config = myService.config;
// You can manipulate various config options now through direct binding.
}
However remember that AngularJS services are singletons, which means they will all share the same state. If you need different state, or need a "new" one each time you will want to do something more like the way $resource or $http works which is basically a factory.
function myController(myService, $scope) {
$scope.config = { value1: 'default', value2: 'default' };
var thisService = myService($scope.config);
// You can manipulate various config options now through direct binding.
}
Just remember that services are basically objects and you can manipulate them as per your design as you need. So these are probably not the only, or even necessarily the best way to accomplish your goal. You have total flexibility here.
I don't think a service's provider is what you are looking for here because it is precisely just what you describe it.
As Chris stated, angular services are singletons. However, if you want your service to output "instances" based on user input, I like the following approach.
function myController(myService, $scope) {
var config = { value1: 'default', value2: 'default' };
$scope.newInstance=myService.create(config);
}
app.service('myService', [function(){
function serviceInstance = function (config){
//take config and return output object
}
return {
create: function(config){
return new serviceInstance(config);
}
}
}]);
I haven't thought about being able to manipulate the config variables as Chris suggested. I don't think it would work in my example but you could data bind to the $scope.newInstance

Initializing/populating select boxes on page load in AngularJS

I'm writing a web application using AngularJS. I use a third-party library (that provides an Angular service) to fetch values from a database, and then use those to initialize some dropdown/select boxes on a page.
So, I have simple select boxes like this:
<div ng-controller="ChoiceCtrl">
<select ng-model="selectedFoo" ng-options="foo in foos"></select>
<select ng-model="selectedBar" ng-options="bar in bars"></select>
</div>
And a corresponding controller that initializes the choices for the select boxes. The service I'm using calls the given callback function after it receives values from the database. (The callback functions could be refactored into one but I'm using separate ones for clarity.)
angular.module('choice').controller('ChoiceCtrl', function($scope, ThirdPartyService) {
$scope.selectedFoo = '';
$scope.selectedBar = '';
$scope.foos = '';
$scope.bars = '';
var fooCallback = function(result) {
$scope.foos = result;
$scope.$apply;
}
var barCallback = function(result) {
$scope.bars = result;
$scope.$apply;
}
ThirdPartyService.asyncGetData('fetchFooOptions', fooCallback);
ThirdPartyService.asyncGetData('fetchBarOptions', barCallback);
});
The database calls are asynchronous and finish after the page has been rendered for the first time, so I manually call $scope.$apply in each callback function.
Is this the right way to initialize dropdown/select boxes in an AngularJS app when the values are fetched asynchronously when loading a page?
I've read tutorials saying that calling $scope.$apply manually is always a "code smell"... But since I'm fetching the values from a database, the operation happens "outside of Angular" which I believe makes those calls justified - and actually necessary.
I'm also wondering if the controller is the right place for these calls. In the tutorials I've read the options are always set in the controller but those sandbox examples never have an asynchronous database call happening.
You should modify three things in your code
The service should return a promise :Refer to documentation of angular for creating promise
Inside service resolve the promise when data is recieved from the server
Inside controller just assign proper values to bar and foos when promise is resolved
Remove $scope.apply since now you are modifying the values inside proper angular scope
Link:Use Promise and service together in Angular

Resources