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.
Related
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);
}]);
I have been playing around with the $location service in angular, with trying to pass certain values to $routeParams to filter a set of items based on multiple conditions like colour, style, category and so forth
My API has a set of filters it can manipulate a group of objects by and one is 'IN' so for example if I want the the API to only return Blue, Red & Green I can do the following:
filter[]=product_colours,in,red,blue,green
If i wanted to just get a certain set of styles in them colours I can do
filter[]=product_colours,in,red,blue,green&filter[]=product_sub_category,in,Long,Short,Slim
I have put together a CodePen trying to prototype my idea of filtering:
CodePen example: http://codepen.io/sutsurikeru1986/pen/YqaeeO
What I am trying to do, but I am having issues with, is trying to maintain the values passed to the query string using &location.search() to be maintained after a page refresh and passing such values to $routeParams so my route can understand the it - as well as preventing the query string from becoming:
?key=param1&key=param2&key=param3
Instead of something a little cleaner like:
?key=param1-param2-param3
Any help to point me in the right direction would be greatly appreciated.
I use this process to save a state object to HTML5 local storage when the page is refreshed or closed. Then I reload the state in the controller (or a factory if you wish) when the page reloads.
// get the state object from local storage
$scope.state = angular.fromJson(localStorage.getItem(id));
// make sure state is an object
$scope.state = (angular.isObject($scope.state)) ? $scope.state : {};
// update your route params in the state object
$scope.state.selectors = $routeParams.selectors;
// when the page refreshes or is closed angular broadcasts a "$destroy" message
$scope.$on("$destroy", function() {
// save the route to html5 local storage
localStorage.setItem("some_state_id", angular.toJson($scope.state));
});
I was wondering how to change the data, but keep the same controller in my angular app. Basically I will have a list of activities (restaurants, parks etc...) when I click on one of these activities, The view will display all the restaurant, and same thing for the parks. I know how to do that, but I would need to create a park_ctrl and a restaurant_ctrl, and since the data will be formatted the exact same way. I just wanted to know if I could use only one controller and just change the data that it receives when I click on those buttons.
I hope my question makes sense.
logic around retrieving data should be the responsibility of services, so I guess you'd just call a different service in the different cases, from the same controller
I think it's not a really good idea, but opinion based.
You can make a function :
function($scope){
$scope.changePage = function (type) {
if(type==="park"){
$scope.parks = asynLoadFunctionToGetParks();
}else{
if(type === "restaurants"){
/* same as below */
}
}
};
}
And changing the type in your view with :
<button ng-click="changePage('parks')">Parks</button>
<button ng-click="changePage('restaurants')">Restaurants</button>
<div ng-if="type==='park'">
{{parks}}
</div>
<div ng-if="type==='restaurants'">
{{restaurants}}
</div>
I think the issue here is that most of the Angular examples available are of the "hello world" variety and so they show retrieving data directly from the Controller. The problem is that AngularJS out of the box doesn't really have a business logic layer itself, and I think most people who have added such a layer are too busy to be putting up examples.
The way I'd do this is to create a "master" service that can get all of the different data types either up front in the Run block or lazily as the user navigates the app, depending on your needs. Then I'd supply a reference to the applicable sub-collection in the route resolution (resolve property) or the isolate scope in the case of a directive.
Alternatively, the controller can ask for the data by calling masterService.getCollection($scope.collectionName) or something like that, but if you do that you run into the issue that masterService may not yet have that particular collection yet and then you have to clutter up your controller with all the promise resolution stuff as if it were a Controller's responsibility to handle that.
You could avoid that by binding to masterService.collections[$scope.collectionName] in the View, which would leave the Controller only exposing the collection on the $scope or controllerAs variable and the masterService still responsible for retrieving the data and making it available.
Yes you can. Just use different service and a common variable in the scope.
if (something) {
$scope.data = restaurantsService.get();
} else {
$scope.data = parksService.get();
}
I make a query to Parse.com to receive a lot of objects and then display all the objects in a view.
I now want to be able to click on one of the objects, which should load a new page where I can edit them.
My questions is, how do I pass one of the objects from the main view to a detail view where I can edit it?
When I tried:
With route, passing the whole object, but this seems like bad practice, and the whole object is printed out in the status bar:
// Route
.when('/job/:object', {
<a href="#/job/{{job}}" style=" ">
By just passing the objectId, and when query the database to get the object by it's id once the detail view is loaded.
.when('/job/:objectId', {
<a href="#/job/{{job.id}}" style=" ">
// Get Object by its ID from Parse.com.
Save the whole array of objects in the $rootScope in order to access them from the new detail controller.
Thanks in advance
as SSH said you need to create one angular service which can hold the object , and then when you move from one view to another simply inject the service and access the object.
Here is how you should do this.
.service('holdobj',function(){
var myobj;
this.set= function(obj){
myobj = obj;
};
this.get= function(){
return myobj;
};
});
now in your controller
.controller(function(holdobj){
$scope.setObject = function(obj){
holdobj.set(obj);
}
});
And then when you want to get object simply call holdobj.get();
You should save an incoming object in a service, and then use that service across different controllers
As long as you are using a shared controller between views I think you could probably set a model before you change the view.
I don't know if there is a way to pass model between controllers when checking router otherwise, without going the old-fashioned way and passing an id to the object, where the second controller would look it up based on that.
From my understanding, the differences is the callback functions to events on an AppRouter should exist in the Controller, instead of the same Router object. Also there is a one-to-one relationship between such AppRouter & Controllers, all my code from Router now moves to Controller, I don't see too much point of that? So why use them? I must be missing something?
The way I see it is to separate concerns:
the controller actually does the work (assembling the data, instanciating the view, displaying them in regions, etc.), and can update the URL to reflect the application's state (e.g. displayed content)
the router simply triggers the controller action based on the URL that has been entered in the address bar
So basically, if you're on your app's starting page, it should work fine without needing any routers: your actions (e.g. clicking on a menu entry) simply fire the various controller actions.
Then, you add on a router saying "if this URL is called, execute this controller action". And within your controller you update the displayed URL with navigate("my_url_goes_here"). Notice you do NOT pass trigger: true.
For more info, check out Derick's blog post http://lostechies.com/derickbailey/2011/08/28/dont-execute-a-backbone-js-route-handler-from-your-code/ (paragraph "The “AHA!” Moment Regarding Router.Navigate’s Second Argument")
I've also covered the topic in more length in the free preview of my book on Marionette. See pages 32-46 here: http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf
I made some override for the router. And currently use it in this way (like Chaplin):
https://gist.github.com/vermilion1/5525972
appRoutes : {
// route : controller#method
'search' : 'search#search'
'*any' : 'common#notFound'
},
initialize : function () {
this.common = new Common();
this.search = new Search();
}