I have an app and I am utilizing routes to load two different views.
The first route #/ is the main list view. That works every time.
My second route is #/view/:id and loads my second view when a list item is clicked.
The view is data driven based off the id, all of which need to make $http requests through a service I created.
Is it possible that when I leave the #/view/:id route, it unloads that view completely and reloads it when I come back?
OR
How do I go about resetting promises inside a service so that the next time the view is loaded, it will request the new data?
These are not mutually exclusive questions. Routing with ngView destroys the view, scope, and controller on every load, so controller and scope history are not preserved among routes. Otherwise, memory load in the browser would be high; instead, we can use services to preserve the state we need to across all routes.
$http requests are made fresh every time they are called, unless the cache option is enabled. There shouldn't be any work required for this to work as designed:
.factory( 'dataService', function( $http ) {
return function dataServiceFn ( id ) {
return $http.get( '/myapi', { params: { id: id } } );
};
});
When your data service is called (e.g. dataService( $routeParams.id ); ), a new promise will be returned.
This is just a sample service structure for an incredibly simple case, but so long as the $http call is made fresh on each service call, you will get a new promise guaranteeing fresh data every time.
Angular will unload the controller when you leave to another controller. If you need to store the data between different view, you should store it inside the service (which is not unloaded when the controller changes) or be prepared to make an $http request every time you load the view.
Related
I am using route.navigate to go through pages, and I don't want to pass data in route params, because it is an object, or even array of items. But every time I try to set data in service and get automatically onInit when I get routed to another page I get undefined variable.
This shows, when I scan an item it automatically launches api to get that item data, and I want to pass that data to a page that is routed to
this.inv.getInvItems(this.itemKey).subscribe(data =>{
this.inv.setInvItem(data);
this.router.navigate(['/itemTake']); })
after this code it reroutes to item take
and setInvItem looks like this
setInvItem(data){
this.invItem = data;
}
but when I go to other page and trying to get that set data from same service it is undefined
console.log(this.inv.invItem);
any ideas? advices?
Pages are not child/parent type, it doesn't stay in same space it gets rerouted
App is being made in newest angular 7 cli
After 3 days of killing all my brain cells for not knowing this answer, now everything works fine, just for some reason it was cached or something, needed to launch on incognito and everything worked just fine
I am using React with react-router and Reflux as my datastore, but I am unsure on how to best deal with persistence to allow page refresh.
My components connect to the store with Reflux.connect, but since the store fetches the data from a backend, it is not available yet when the Components first initialize and render.
When the user enters my app from the start, then all this data is loaded in order and available when it needs to be, but if further down a route you trigger a page refresh, react tries to render components that rely on data that is not there yet.
I solved this by constantly keeping a copy of data in LocalStorage and serving that from the Reflux store getInitialState(), so that all components get the data before they render.
I wonder if this is the proper way to do it. When for some reason the local storage data gets cleared or corrupted, the interface goes blank, because the components cannot access the correct data. Substructures and properties don't exist and trigger javascript errors. It seems like a messy and unreliable solution.
I am curious to know what patterns are used to solve this.
------ edit -----
To answer to the comment of WiredPrairie:
1) Why are you initializing components with data in getInitialState?
When my components use Reflux.connect, they don't have the data in their state yet on the first render as the store still needs to fetch its data. My views currently don't work gracefully with undefined data. By returning the locally stored cache from the Reflux store in getInitialState(), all connected components will get that data before their first render call.
2) What's causing a page refresh and why can't the data be loaded in the same manner as it was the first time?
It's mainly a workaround I had to build around livereload refreshing the page when I make edits (will look into using react-hotloader later but is not an options yet), but users can also just hit refresh when they are somewhere in my nested views and that would have the same effect. When they manually refresh, they are not entering the app at the start.
3) When components are wired to the change events of a store, why don't they update then?
They do update, but like I said they don't deal with empty data right now and on first render they will miss it waiting for the store to fetch things. I can make all my views work gracefully with empty data, but that would add a lot of boilerplate code.
From the replies so far, I get the feeling that what I'm doing with localStorage is the common way to do it. Cache stuff locally in localStorage or sessionStorage or something similar, and serve that data immediately after a refresh.
I should make my views a bit more robust by gracefully handing empty data on the odd occasion that localStorage doesn't work properly.
I've been caching each Store in sessionStorage when its emitChange() fires, and initializing the store from sessionStorage if cached data exists, or null values otherwise. This seems to work provided that the views can handle null values, which is probably a good idea anyway (it sounds like this is your main problem).
I'd suggest making your views handle the case of no data gracefully, initialize everything with null values if the cache isn't available, and then call the backend to update the null values with an Action whenever the data returns.
I haven't tried Reflux, but in regular Flux it would look like this (maybe you can apply the same principle):
var _data;
if (sessionStorage.PostStore)
_data = JSON.parse(sessionStorage.PostStore);
else {
_data = {
posts: null
};
BackendAPI.getPosts(function(err, posts) {
if (posts) {
PostActions.setPosts(posts);
}
});
}
...
AppDispatcher.register(function(payload) {
var action = payload.action;
switch (action.actionType) {
...
case Constants.SET_POSTS:
_data.posts= action.data.posts;
break;
default:
return true
}
// Update cache because state changed
sessionStorage.PostStore = JSON.stringify(_data);
PostStore.emitChange();
return true;
});
Can you run an Angular service (or a function on that service) before anything else? Ideally, as soon as ng-app gets parsed.
Here's my use case: I'm writing an app that gets AJAX data from a server and then parses the data a hundred different ways. I would like to make the initial AJAX call before all the controllers get called? That way I just have all the data parsed and loaded in the service without me worrying about updating any controllers or whatever.
I would like to make the initial AJAX call before all the controllers get called
In Angular method run is fired before any controller is called
var app = angular.module('myApp',[]);
app.run(function($rootScope){
// ajax call and other stuff
}
In run method you can do any job like login to Facebook, token validation and so on
Reference
Configuration blocks (aka app.config) - get executed during the provider registrations and configuration phase. Only providers and constants can be injected into configuration blocks. This is to prevent accidental instantiation of services before they have been fully configured.
Run blocks (aka app.run) - get executed after the injector is created and are used to kickstart the application. Only instances and constants can be injected into run blocks. This is to prevent further system configuration during application run time.
docs.angularjs.org/guide/module
plnkr = http://plnkr.co/edit/WTNuWKSgj0bMR1dtUkto?p=preview
The best way to configure how your services behave is to use providers. so, assuming you already have a mydata from your ajax call, the plnkr above shows a running example...
myapp.config(['sayHelloProvider',function(sayHelloProvider){
// assuming your ajax retrievies mydata
var mydata = angular.fromJson( angular.element(document.getElementById('mydata')).html() );
// configure service
sayHelloProvider.SetMessage("Olah! rate is=" + mydata.rate);
}]);
I have a completely separate (from backend) AngularJS app. It uses tokens (JWT) for authentication. I'm also using ui-router and Restangular.
My problem:
I have a sidebar/profile area that displays information from the currently logged in user on every "page". It is implemented as a directive+controller and is outside of the ui-view directive context. Inside of the controller for this sidebar I'm making a request to get the profile information and attach it to the $scope. Now, when a user first visits the app that sidebar and controller get invoked and the request is made to get the profile information. However, if and since the user is not logged in, the request fails and no data is bound with the $scope.
In the meantime (notice this all happens really fast), the event listener (listening for $stateChangeSuccess from ui-router) determines that the user is not logged in (essentially if they don't have a token) and does a $state.go('login') to forward the user to the login view. The user logs in, and a $state.go('dashboard') is invoked to bring them back to the dashboard. However, being that sidebar is outside of the context of the router the controller for it is not instantiated again to cause a new request for profile information. So the sidebar is just empty with no profile information.
I'm aware that there are going to be several possible solutions to this, but I'm trying to find a descent one.
How would you architect an angular app in order to solve or avoid the problem I'm having?
P.S. I think I may be having analysis paralysis over this.
It's hard for me to answer without seeing your code specifically. If I understand correctly your directive is firing prior to the user logging in, and since there is no user profile, the side bar doesn't initiate correctly. What I would suggest is possibly doing an ng-if on the tag that fires the directive something like:
<side-bar ng-if='userData' />
That way the tag isn't inserted into the DOM until the userData exists and therefore doesn't fire the directive on the login page.
Assuming that the sidebar is the highest angular controller in your application and the other controllers are nested inside it you should be able to put a function on it that will load the information that you need. Then you can call $rootScope.loadme() anywhere you inject $rootScope.
var mainMod = angular.module('mainMod',[]);
mainMod .controller('mainController', function($scope)
{
$scope.loadMe = function()
{
//load stuff here
};
$scope.loadMe();
});
mainMod .controller('secondController', function($rootScope, $scope)
{
$rootScope.loadMe();
});
SudoCode probably wont work with copy paste but the idea should be sound.
I am using AngularJs for a new application. I feel I have solved a problem but i'm not sure i have done it in the best way possible so would like to check before gunning ahead.
Let's for examples sake say i have the two controllers AccountsCtrl and ContactsCtrl, every time each is called a request to a REST server for all the accounts or contacts is made for each controller respectively. When within the controller any data changes are kept in sync in the angular models (and the server backend) to reflect this and hence the UI. Every time the user switches between each controller they have to make call to the server to fetch the data which it already had (which was up to date) last time it was loaded.
Currently this causes a very small lag. I would like to make it persistant i.e. not loaded each time the controller is loaded to save on the lag and the server requests. I have tried saving the data into the $rootScope and this works great but i'm not sure it's the right thing to do?
The question is how best solve this problem? Is the $rootScope the best way to tackle this?
I would store the data and code that interacts with the web server in one or two Angular services. Each time a controller is created (e.g., when you go back to the page a second time), the (appropriate) service should decide whether to return the cached data or make an Ajax request to your REST server.
Your service(s) would be injected into your controllers.
See also https://stackoverflow.com/a/12009408/215945 and https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion
First of all, you should do your REST call via a service:
app.factory('RestService', ['$http', function($http) {
return {
getSomething: function(url) {
var result = {};
$http.jsonp(url).success(function(data) {
result.list = data;
});
return result;
}
};
...
}];
You don't need $rootScope. Just define a parent controller which scopes both controllers, e.g. RootController, and store the data there. This is because child scopes (e.g. from AccountsCtrl) inherit what is defined in parent scopes.
<div ng-controller="RootController">
<div ng-controller="AccountsCtrl">
</div>
<div ng-controller="ContactsCtrl">
</div>
</div>
In practice this is almost the same as using $rootScope, but you don't need to inject it and you keep your controller logic similar as other controllers.