Loading data at startup and displaying via controller in AngularJS - angularjs

I have a service which loads some data and then stores it into local storage, for a Angular base mobile app. I'm calling this service in the run function of my main JS file, like this:
angular.module('myApp', ['ionic'])
.config(function ($stateProvider, $urlRouterProvider) {
})
.run(function (StartupService) {
StartupService.initialDataLoad();
})
This data loads in to local storage, and then in the init function of the first views controller I try to access this data in local storage and display it in a list:
angular.module('MemberInduction').controller('InductionListCtrl', function($scope, DataAccessService) {
$scope.init = function init() {
if (localStorage.getItem('MI.ApplicationData') != null) {
var data = DataAccessService.getInductionList();
$scope.inductionsList = data;
} else {
$scope.displayReloadMsg = true;
}
}
$scope.init();
});
As you can see in my init() function I access the data through a service, but when the controller initialises the data maybe loaded in localStorage, but at the time of initialisation the data isn't there. So my displayReloadMsg flag is set to true, causing a message to the user telling them to reload the app to display the loaded data.
How can I get the view to initialise once the data has loaded successfully? So that the view and controller only load from localStorage once the data has loaded?
Thanks
Stephen

you want to use resolve on your main controller to make sure the data is loaded prior to accessing the controller.
See this example: https://thinkster.io/egghead/resolve-conventions
In the example below, the state transistion will not happen if the Flickr.search() returns no data - full source for this example is here - http://codepen.io/aaronksaunders/pen/gboQVO
.state('flickr-display', {
url: '/flickr-display:query',
templateUrl: 'flickr-display.html',
controller: 'FlickrDisplayCtrl as flkr',
resolve : {
ImageData : function($stateParams, Flickr) {
return Flickr.search($stateParams.query);
}
}

Related

AngularJS ui-router - view disappears on reload

Recently I started to use ui-router to manage my page status.
I noticed that when I run the server for the first time, the content is there but if I navigate through the pages or reload the page the contents disappears.
I'm aware there was a bug on ui-router that could be causing this. I updated ui-router however the error still persists.
Did anyone manage to find a fix or work around it?
Edit ----
My investigation has lead me to believe that it has nothing to do with the ui-router and more on the time the app takes to complete the promise. As I will get Violation warnings on the setTimeout as part of jQuery
So I was partially right on my last edit hence this answer; jQuery had something to do with my problem (through the use of promises to retrieve data), however it had also to do with how the ui-router works:
As the page content is loaded, ui-router will manage the first load (and subsequent reloads) with the data that is provided by the GET request.
As this is a promise it is not guaranteed (especially as you grow your DB) that the data will be there in time to render the page.
To avoid from happening use the resolve property as part of ngRoute. This property allows for all the necessary data to be loaded before rendering the DOM.
If you want to read more about follow the link below:
https://medium.com/opinionated-angularjs/advanced-routing-and-resolves-a2fcbf874a1c
My code below:
App.js
'use strict';
angular
.module('knoweeApp', ['ui.router'])
.config(['$stateProvider','$urlRouterProvider',function($stateProvider,$urlRouterProvider) {
$stateProvider
.state('home', {
url:'/',
templateUrl: 'views/user.html',
controller: 'UserCtrl as user',
resolve: {
user: function(userFinder) {
return userFinder.getUsers();
}
}
})
.state('teacher', {
url:'/teacher/:name',
templateUrl: 'views/teacher.html',
controller: 'TeacherCtrl as teacher'
});
$urlRouterProvider.otherwise('/');
}]);
Service: userFinder
'use strict';
/**
* #ngdoc service
* #name knoweeApp.userFinder
* #description
* # userFinder
* Service in the knoweeApp.
*/
angular.module('knoweeApp')
.service('userFinder', function () {
// AngularJS will instantiate a singleton by calling "new" on this function
this.getUsers = function (){
return $.get('users.json');
};
});
Controller: UserCtrl
'use strict';
angular.module('knoweeApp')
.controller('UserCtrl', function (userFinder,user) {
this.teachers = user;
this.increment = function (item) {
item.rating += 1;
};
this.decrement = function (item) {
item.rating -= 1;
};
});
Hope this helps, contact me if in doubt

Dynamically change the template url in angular js

I'm developing a SPA in which data is stored in a JSON file. The JSON file also contains the link to the pages including ID. Whenever the user searches for an ID, the content of the page corresponding to that id should be loaded. How to do it?
Your search implementation should be separate, perhaps with a directive. There is no good answer to that, unless you have something to work with already.
However, the dynamic template loading can be achieved with a method that allows you to read URL parameters, such as ID, and re-direct accordingly. One of these methods is $location, whereas the best one for your case is $routeParams.
Here is an example:
const app = angular.module("app", ['ngRoute']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/some_path/:ID', {
templateUrl: 'templates.html',
controller: 'templateCtrl'
});
}
(For more advanced control, switch ngRoute to ui-router library and adjust accordingly)
With this once your URL is in a form: www.example.com/some_path/10, it will re-direct you to that page with a $routeParams.ID value of "10".
In your template controller you can then use $routeParams to load your JSON file and fill in the $scope values of the page accordingly.
app.controller("templateCtrl", function ($scope, $routeParams, $http) {
var id = $routeParams.ID;
$http.get('myFile.json').
then((res) => {
// match your ID and load the data
let data = res.data;
let model;
for (let i = 0; i<data.length; i++){
if (data[i].id == id){
model = data[i].content;
break;
}
}
$scope.model = model;
});
});
If your want to wait until the data is loaded, before you display your page content, then use resolve in the config with appropriate factories / services that load your JSON files.

Refreshing Resolve Data - Ui Router

In ionic I'm resolving my data with the Ui-Router's resolve functionality before the controller is initialized. As of now I don't have to $inject my EventService into the Controller. The EventService's getEvents() method resolves the data before the controller is initialized. Everything works correctly this way, but now i'm trying to implement the Ion Refresher. I could easily refresh my $scope.events array within the controller, bloating the controller itself, because I would have to $inject the EventService into the controller, and that also means that every controller that uses the same data will have to contain logic to handle a refresh. What is the best way refresh the data outside of the controller or is that the best way?
Events State Definition and data resolution
.state('tab.events', {
url: '/events',
views: {
'tab-event': {
templateUrl: 'views/events.html',
controller: 'EventsController',
resolve: {
events: function (EventService) {
return EventService.getEvents(); //resolves data before ctrl initialized
}
}
}
}
})
Events Controller
(function() {
'use strict'
angular
.module('app.events')
.controller('EventsController', EventsController);
EventsController.$inject = ['$scope','events'];
function EventsController ($scope,events) {
$scope.events = events;
}
}
)();
Bloated Events Controller - Example
(function() {
'use strict'
angular
.module('app.events')
.controller('EventsController', EventsController);
EventsController.$inject = ['$scope','events','EventsService'];
function EventsController ($scope,events,EventsService) {
$scope.events = events;
$scope.refresh = refresh;
function refresh () {
clearCache(); //pretend method
EventsService.getEvents()
.then(function (events) {
$scope.events = events;
$scope.$broadcast('scroll.refreshComplete');
})
}
}
}
)();
Rather than bloating the controller can I refresh this data another way?
call $state.reload() which is an alias for:
$state.transitionTo($state.current, $stateParams, {
reload: true, inherit: false, notify: true
});
This will cause all your states to be "re-entered" which fetches the resolves and re-initializes the controllers.
I wish a hard refresh, which is basically what a $state.reload() does wasn't the answer. I too have this issue and would rather be able to call some method that just forces all the resolved data objects to rerun. The reload causes a page refresh, which causes nasty UI artifacts.

How to sync data with angular-data

I have a angular project with a angular-data to work with my Restful API.
my situation is as following:
in my service I return a defineresource
in my controller I use service.defineresource.findAll().then
it all works fine but here is my question. when new data is added to the server how do I update the cache or add the new data to my cache.
Please advice
code:
//in service.js
.factory('Service', function(DS) {
DS.defineresource({
name: 'items'
endpoint:'api/v2/users'
});
});
//in controller.js
.controller('MainCtrl', function($scope, Service) {
Service.findAll().then(function (data) {
$scope.items = data
});
});
my code is above fetches all the data when I load it first time, but after the initial loading it only loads from the cache. My question is there a way to sync and fetch new data without destroying the cache.
To force the refresh of the data from the backend you need to set bypassCache: true in the options. If you don't pass this then angular-data will load from cache. This is documented in the api documentation.
To refresh the data every 15 minutes use $timeout with 900000 milliseconds. To have a button to refresh use a scope function that will be called from a ng-click on a button.
.controller('MainCtrl', function($scope, $timeout, Service) {
$scope.refreshData = function () {
Service.findAll({}, { bypassCache: true }).then(function (data) {
$scope.items = data;
});
}
$scope.refreshData();
$timeout($scope.refreshData, 900000);
});
<button ng-click="refreshData()">Refresh</button>
If you are using only Angular Routes, Use :
$route.reload()
it will reload / refresh your page . so the data will be sync .
you can use it in ng-click or check in your controller when new data is entered use it
$route.reload()
if you are using UI-Router you can do the same but instead of $route.reload() use this :
$state.go($state.current, {}, {reload: true});

Is there a way to hold off rendering the AngularJS view before all the AngularJS $scope data has been retrieved?

This might be a beginner question, but I am retrieving data via http calls in AngularJS and setting them as properties in the $scope variable. However, since http calls take a while, my page tries to load AngularJS more than once in order to render different parts of the page as more the data is retrieved. Is there a way around this? (to hold off on loading the page before all data has been retrieved)
What you could do is to use ng-hide or ng-cloak, so that whatever should not be displayed until the http call fully loaded the data would remain hidden.
take a look at the resolve property in the route settings. If you set something to be resolved the router will resolve this before going to the controller.
app.config(function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: "app.html",
controller: "AppCtrl"
resolve: {
app: function ($q, $timeout) {
YourFactory.getData({});
}
}
}
)
});
then create a Factory that will get the data you need
app.factory('YourFactory', ['$http', '$q',
function($http, $q) {
var url = '/api2.php/api';
var YourFactory = {};
var factory_data = [];
var messages = [];
YourFactory.getData = function(params) {
console.log("into GET data");
var deferred = $q.defer();
$http.get(url).success(function(response) {
angular.copy(factory_data, response.data);
deferred.resolve();
}).error(function(response) {
//couldn't resolve therefore it's rejected
deferred.reject();
});
//returns a promise that indicates that something is being resolved and will be returned for the app to continue
return deferred.promise;
};
YourFactory.data = function() {
return factory_data;
};
return YourFactory;
}
]);
then in your controller you need to input the factory and set the scope data from the Factory. Remember that Angular used the Factory to get data before the controller using the resolve property.
app.controller("AppCtrl", ['$scope','YourFactory',
function($scope, YourFactory) {
$scope.data = YourFactory.data();
});
(I haven't tested the code, I simply wrote an example based on an app that I'am doing and in which I passed through the same things as you)
Look at this links if you have any doubt.
https://egghead.io/lessons/angularjs-resolve
http://www.javierlerones.com/2013/07/preloading-data-using-deferred-promises-in-angular-js.html

Resources