So I am relatively new to AngularJs and I am trying to figure out the best way for a tabController to remember what tab was previously clicked when switching to a new controller. So the situation would be I have 3 tabs. I click on tab 3 and then I click on something inside of it bringing me to a new controller and HTML template... What is the best way if I hit a "back" Button I created in that controller to remember exactly the state of it being the 3 tab.
I tried using $rootScope and then in each controller setting the tab number and setting the tabcontroller = $rootScope... but that was chaotic and too repetitive, and its not the right way.
This is not about $windoe.back(), this refers to coming up with a way that no matter where the navigation is the tab number is retained.
You can use a factory for this. In angular, a factory is a singleton, meaning only one instance of it exists for the whole project. Thus, by making something with it in one controller (and saving what you've did), you can access your changes in another controller.
angular.module('awesomeApp')
.factory('tabHistoryFactory', function () {
var tabHistory = {
setPrevTab: function(tab) { tabHistory.prevTab = tab; },
getPrevTab: function() { return tabHistory.prevTab; }
};
return tabHistory;
});
Then, in your first controller you'll have to inject this factory and before changing to another tab, just save the tab you're on using tabHistoryFactory.setPrevTab(tab). Then, in your second controller, you can access your previous tab by using tabHistoryFactory.getPrevTab(). Similarly, you can customize the behavior of your tab history by implementing other functions alongside those two.
Good luck!
Related
I have created a plunker that communicates between two different controller in nested views using factory. Below is the url of plunker.
https://plnkr.co/edit/fWA2Xugjbkf3QvHKfTa0?p=preview .
Here is the factory.
routerApp.factory("widgetService",function($state){
var callbackFunctions=[];
var counter=0;
var addWidget=function(name){
callbackFunctions[0](name);
}
var addCallback=function(callback){
if (callbackFunctions.length===0) {
callbackFunctions.push(callback);
}
}
return{
addCallback: addCallback,
addWidget: addWidget
}
})
Scenario 1:
1. Click on "List" menu under the home page.
2. Click on "verify" button. notice the change in highlighted area with yellow. Text changes from "Chandan" to "singh1".
3. Click again on verify button. Text changes from "singh1" to "singh2". So i am able to communicate between two controller in this.
Scenario 2:
1. Repeat the scenario 1 till step 2.
2. Click on "PARAGRAPH" menu under home page.
3. Click on "List" menu under home page. Click verify button. The text wont change. It will remain "chandan".
Communication is not working as we change the state.
Also i have observed that the model is changing but same is not getting reflected in view. Moreover if we bind the view to rootscope, view is getting updated.
Please clarify.
When you switch between list and paragraph states, your list controller is destroyed (and recreated next time you go to list). So when it is recreated it gets a new scope.
The callbackFunction that is registered in your service, refers to the changeMyName() function on the old controller scope, so whenever you call addWidget() in your service, you're actually calling the callback in your old scope.
To make this work as expected (at least in your sample code here), you would have to change your service to allow changing the old callback in the addCallback()-function. Something like this:
var addCallback=function(callback){
callbackFunctions[0] = callback;
}
While transitioning an existing angular site, I encountered an annoying problem. The initial symptom was that a certain controller was not running it's initialize function immediately following the login. I logged and I tracked, and eventually I realized it was a design flaw of the page. Essentially, index.html contains a <header>, <ng-view>, and <footer>. There are a couple of ng-if attributes that live in the header that I want to evaluate after the login, but since the view is the only thing that is reloaded, it was not reinitializing the header controller, and thus not updating the ng-if values.
Then I was reminded of ngInclude, which seems like the perfect solution, until I got it hooked up and realize that doesn't work either. It loads the template the first time, and doesn't reinitialize when the view changes. So then I got the bright idea of passing the HeaderController to another controller or service, and controlling this one stubborn boolean value through a proxy of sorts. That also didn't work. Then I tried putting a function and a boolean into another service, and mirroring that property in the header controller, but thus far I have not gotten this working.
I have done plenty of research about multiple views in the index, and so far I hear a lot about this ui-router, but I'm still not convinced that is the way I want to go. It does not seem to be a simple solution. I have not tried putting the ng-include into the templates yet either, because then I feel like that is going back in time to when we had to update 100 pages every time we changed the menu.
I lost a whole day to this. If anyone could tell me how to trigger the evaluation of this one property in my header controller which I would like to live outside the other templates, please let me know!
Ok so you need to know in your HeaderController when the view has reloaded. There's a number of ways of doing this but the easier and maybe the more correct in this particular case is with an event.
So when you are refreshing the view you just do this, let's say you need the new value of ob1 and ob2 variables.
// ViewController
$rootScope.$emit('viewRefresh', {ob1: 'newvalue1', ob2: 'newvalue2'});
And in your HeaderController you need to listen for that event, and set on your $scope the new values for those attrs (if you're not using controller as syntax).
// HeaderController
$rootScope.$on('viewRefresh', function onRefresh(event, data) {
$scope.ob1 = data.ob1;
$scope.ob2 = data.ob2;
})
Another Solution
Sharing a Promise through a Service (using $q)
function HeaderService($q) {
var defer = $q.defer();
return {
getPromise: function() {return defer.promise},
notify: function(data) {defer.notify(data)}
}
}
function HeaderController(HeaderService) {
var vm = this;
HeaderService.getPromise().then(function(data) {
vm.ob1 = data.ob1;
vm.ob2 = data.ob2;
})
}
function ViewController(HeaderService) {
var data = {ob1: 'newvalue1', ob2: 'newvalue2'};
HeaderService.notify(data)
}
I'm fairly new to angular, so bear with me. :-)
I have a list of contacts in one view. I put together a service with some setter and getter functions to hold the contact ID. When the user clicks a contact on the list view, it sets the ID in the service and moves to the edit form view. This is working well, but when I'm done editing and click to go back to the list view I want to clear the variables in the service. How would I do that?
I found one other answer on this topic, but I think it's using the built in angular router instead of ui-router.
Angular updating and clearing factory variables
Also, wouldn't the method in that answer also clear the variables when the user moved from the list to the edit form in the first place?
Thanks!
If it's a route change you can use $locationChangeStart, see $location
For example in your edit controller, register for the event on route change:
var onloc = $scope.$on('$locationChangeStart', function (event, newUrl, oldUrl) {
myService.cleanUp(whatever);
onloc();
});
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.
I have created a sidemenu based app, in that after login I am displaying a number of tasks. If I click on the task it will redirect to the task details page, in that page I can update the tasks.
So after updating a task I need to go back to the previous task list page. I am using $ionicHistory.goBack(); to go back.
My problem is after come back, I need to refresh the task list i.e. updated task should not be there in the task list. How can I refresh/reload the task list?
If you bind your task to a tasks array, which will be used in the task list page, it should be automatically updated.
But the question is about not displaying, newly added tasks (still my previous suggestion should work) if not, performance reasons ionic views are cached, So when you come back to the previous view it doesn't go through the normal loading cycle. But you 2 options
1 - disable the caching by using <ion-view cache-view="false" view-title="My Title!"> in your ion-view, but this is not a very elegant solution. read more
2 - use ionRefresher (my preferred). read more here
https://github.com/angular-ui/ui-router/issues/582
according to #hpawe01 "If you are using the current ionicframework (ionic: v1.0.0-beta.14, angularjs: v1.3.6, angular-ui-router: v0.2.13), the problem with the not-reloading-controller could be caused by the new caching-system of ionic:
Note that because we are caching these views, we aren’t destroying scopes. Instead, scopes are being disconnected from the watch cycle. Because scopes are not being destroyed and recreated,controllers are not loading again on a subsequent viewing.
There are several ways to disable caching. To disable it only for a single state, just add cache: false to the state definition.
This fixed the problem for me (after hours of reading, trying, frustration).
For all others not using ionicframework and still facing this problem: good luck!"
Hope this helps.
You can also listen to ionic events such as $ionicView.enter on $scope and trigger the code that refreshes the list if you haven't bound your list as #sameera207 suggested.
EG:
// List.controller.js
angular.module('app')
.controller('ListController', ['$scope', function($scope) {
// See http://ionicframework.com/docs/api/directive/ionView/ for full events list
$scope.$on('$ionicView.enter', function() {
_someCodeThatFetchesTasks()
.then(function(tasks) {
$scope.tasks = tasks;
});
});
});
Bear in mind that it's not the most proper way (if proper at all) and if you do this you certainly have a design flaw. Instead you should share the same data array via a factory or a service for example.
For your task you can also use ion-nav-view.
It is well documented. And if you are using now Ionic 2 beta you can use some of the view lifecyle hooks like onPageWillLeave() or onPageWillEnter(). I just faced the same problem and defined a refresh() function, but the user had to click on a button to actually update the view. But then I found:
https://webcake.co/page-lifecycle-hooks-in-ionic-2/
You just have to import the Page and NavController module and also define it in the constructor. The you can use for example onPageWillEnter(), which will always invoke when you go again to a view:
onPageWillEnter() {
// Do whatever you want here the following code is just to show you an example. I needed it to refresh the sqlite database
this.storage.query("SELECT * FROM archivedInserates").then((data) = > {
this.archivedInserates =[];
if (data.res.rows.length > 0) {
for (var i = 0; i < data.res.rows.length; i++) {
this.archivedInserates.push({userName:data.res.rows.item(i).userName, email:
data.res.rows.item(i).email});
}
}
},(error) =>{
console.log("ERROR -> " + JSON.stringify(error.err));
});
}
With ionic beta 8 the lifecylcle events changed their names. Check out the official ionic blog for the full list of the lifecycle events.
if you are building data driven app then make sure use $ionicConfigProvider.views.maxCache(0);in your app.config so that each review can refresh for more details read this http://ionicframework.com/docs/api/provider/$ionicConfigProvider/