Ionic and Angularjs - Update/Refresh View from Controller - javascript

The usual start to these, I am new to both Ionic and Angularjs. I am developing an Ionic app which at it's heart is very simple. We show a list of classes(sessions), the person clicks on an icon to book the class then the icon changes to allow them to cancel the class. We also update the card to show the number of places remaining in each session on the day.
I have the code working to add and remove a person to and from a class but I am not sure how to update the template view from within the controller.
The controller code is pretty simple
// Check Person in to session.
$scope.addCheckIn = function(schedule){
var promise = sessionDataService.checkinSession(schedule.sessionID);
promise.then(function(data){
// Update (refresh) Schedule Details
// NOT SURE WHAT TO PUT HERE??
});
};
I have tried a number of different approaches including
Refreshing the $state and calling doRefresh and even calling the original controller methods to populate the cards again but the view won't update unless I physically click between states on the screen
//$state.go('app.schedules', {}, {reload: true});
//$scope.doRefresh();
//getScheduleData(formatDate(selectedDate), formatDate(selectedDate), 'true');
I have also looked at $scope.apply and $scope.timeout but I am not sure if this is taking me further from the real solution
What is the correct way to update the view after an update? Should it be after the promise.then in the controller or should I call a service and update everything.
Any tips on what is the best way to do this and a point in the right would be really appreciated.
Thanks everyone.

In your promise, you should add the data to the scope.
$scope.scheduledetails = data;
Then in your template, you will be able to access the object scheduledetails from the controller with AngularJS brackets to bind the data to the HTML.
<h1>{{scheduledetails.title}}</h1>
<p>Details : {{scheduledetails.details}}</p>
AngularJS should take care of refreshing what is needed without having to call any method or anything.
Full example
Controller
$scope.addCheckIn = function(schedule){
var promise = sessionDataService.checkinSession(schedule.sessionID);
promise.then(function(data){
$scope.scheduledetails = data;
});
};
Template
<h1>{{scheduledetails.title}}</h1>
<p>Details : {{scheduledetails.details}}</p>

Related

Controller lifecycle and emit/broadcasts

On my page, I have a grid listing all articles returned by a web service. The user can go to a seperate page to add a new article and when finished, the article should be added in the grid on the other page. So when the user returns to that page, the article will be listed.
I have a service that handles the communication between the 2 controllers (one for the grid, one for the adding of articles). This service has a function called by the AddArticleController that broadcasts a message to my GridController:
function addNewArticle(articleNumber) {
$rootScope.$emit('newArticleAdded', {
articleNumber: articleNumber
});
}
The gridController picks this up:
$rootScope.$on('newArticleAdded', function(event, data) {
// get all article details generated by the back-end and add it to he grid
});
The problem is that, when the page with the grid has been displayed multiple times already, the article is added multiple times to the grid. I assume the reason for this is because every time a new controller is created, but the old controller is not being destroyed, so the broadcast is picked up multiple times.
How do I solve this issue? Obviously, the broadcast should only be handled 1 time.
Service
myApp.service('myService',function($controller){
this.broadcastEvent = function(){
scope = $scope.$new();
$controller('mainCtrl',{
$scope:scope
});
scope.broadCastMethod();
}
});
Controller
myApp.controller('mainCtrl',function($scope){
$scope.broadCastMethod = function(){
$scope.$broadcast("callBraodcast");
}
})
Acknowledging that I say this from a position of ignorance; would it not be easier to have the service maintain the list of articles and let the controller observe that? It would avoid the need to send messages back and forth through the scopes and have a single source of truth.

UI updates slowly with setTimeout() angularjs

I am using Angularjs and trying to update my ui. Currently I am making use of these methods to update the ui quickly, but I am getting a 2 second delay before ui changes after controller has been updated with new data.
$scope.sub("panel.service.updateData", updateEventData.bind(this));
function updateEventData(ngEvent, data) {
var errorMessage = "updateEventData() ";
this.timeout(():void=> {
this.scope.list.myList = [];
this.scope.list.myList = data.data;
});
console.log(errorMessage+data.type);
console.log(this.scope.list.myList);
}
I have also tried:
safeApply() method wrapping around the changes in my controller as
well.
wrapping my data inside another object 'list'
updating the controller with a $watch()
updating the controller with sub/pub
I am currently using a sub/pub mechanism to pass data between controllers and services.
I have a console.log() set up right after the call in the controller to show when it was updated with new data, the controller is updated quickly, but my ui doesnt get the changes for about 2 seconds afterwards.
Any ideas?
Yes, you can force Angular to redraw "right now" by calling controller.$apply(). In your code I believe it would be:
this.timeout(():void=> {
this.scope.list.myList = [];
this.scope.list.myList = data.data;
this.scope.$apply();
});

Creating a controller/service to persist data, with information from the DOM

In angular I have in a view a list of items which will be display in another view depending on whether they are checked or not.
this is the view where you check which sports you want to display in the another view
<ion-item ng-repeat="sport in sports"
ng-init="sport.checked = true"
ng-click="sport.checked = !sport.checked">
{{:: sport.name}}
</ion-item>
this is the another view
<div ng-show="sport.checked"
ng-repeat="sport in sports">
{{sport.name}}
</div>
as you see all those sports has an ng-init = sport.checked = true, so all the sports are shown at the beginning but you can unchecked those sports in order to don't see them until you checked it again.
that's all I have so far and works, but once you refresh the page, all the checks and unchecks die, everything restarts after refreshing, so as you know I need to persist that data in a DB or something, I am using node.js/sails.js along with Redis for this. I have an idea of how to create almost everything in the node.js part, but I need some help in the Angular part with the controller and the service, so I need your help here.
I want that the users are able to checked their sports only once, and not every time they log in on the app.
So what functions can I add to the controller to advise to the service when a sport has been unchecked, and how the service will be receiving that data ?
also, if you have any suggestions for the NodeJS part, that would be awesome too.
Here is an example of what you could use. This is very bareboned. All the $http fucntions work with promises. The controller will get the settings from the service when, if that is empty it requests new settings from the service
angular.module('app').controller('appctrl',['appsrv','$scope',function(appsrv, $scope){
$scope.settings = service.settings;
if(!$scop.settings){
//retrieve data from server when it's not in the service
appsrv.getSettingsRemote().then($scope.settings = service.settings);
}
}]).service('appsrv',[$http, function($http){
return {
getSettings : function (){
// get settings from localstorage
},
setSettings : function (){
// store settings to localstorage
},
getSettingsRemote : function () {
$http.get('url').then(//set things locally);
}
}
}]);
Then from your view you could just assign the controller to the view and then reference to your settings like {{settings.propname}}
I don't want to write all your code but maybe this has given you insight in what you could do

Pass another controller when instantiating a fragment in SAPUI5

In the SAPUI5 / OpenUI5 xmlfragment documentation the third parameter is a controller for handling actions from the fragment.
This is critical for a dialog fragment where there are buttons to press etc.
Most of the time I have seen this instantiated as this or sap.ui.getCore().byId('<element>').getController())
See an example at Fragment not get correct Controller
Because of the complexity in a particular dialog I would like to have a separate controller for it.
I have looked around at this and had a few attempts but so far not successful.
I have put a working example on github of using this.
But I would like to instantiate Dialog.js as the controller for the Dialog.fragment.xml from initial.view.controller
Any takers?
Pull requests gladly received.
The Crux of the example is as follows (this is the initial.controller.js) :
sap.ui.controller("sc.test.view.initial", {
oDialog: null,
openTestDialog: function(){
console.log("in open dialog");
// instantiate the other controller
var oDialogController = new sc.test.view.Dialog();
// this next commented line is the 'normal' way to do it
// oDialog = new sap.ui.xmlfragment( "sc.test.view.Dialog", this); //oDialogController);
// this is what I would like to achieve
oDialog = new sap.ui.xmlfragment( "sc.test.view.Dialog", oDialogController);
oDialog.open();
},
onCartDialogCancel:function(oEvent){
// this function would then be in the other controller but how to get a handle on the dialog?
oDialog.close();
}
});
Thanks.
(Just got to SYD airport)
All you're missing is the
jQuery.sap.require("sc.test.view.Dialog");
in your initial.controller.js.
Pushed a quick fix in a branch to your repo and opened a PR
only example i could find close to yours was in the Material Shortage Fiori app
oCtrl = sap.ui.controller("myapp.fragments.DirectCallDialog");
oDirectCallDialog = sap.ui.xmlfragment("myapp.fragments.DirectCallDialog", oCtrl);
lots of examples of injecting a controller when the fragment was called from a helper class. The helper class promotes reuse, eg same dialog fragment can be called from multiple views/components. The helper class method for the dialog setup is called from within a controller and the oController parameter is set to 'this'.
hth
jsp
I copied an existing controller.js, and renamed it.
Then, instantiated that as a below, and passed it through with the fragment.
var oNewController = new sap.ui.core.mvc.Controller("myProject.DialogController");
this._oDialog = sap.ui.xmlfragment("myPopup","myProject.fragments.myPopup", oNewController);
All eventing is now handled in oNewController, rather than the previously used "this"...

ngResource remove record from query object with $delete

Fairly new to angular. I want to use angular's $resource library to consume our API services. I'm a little lost on the proper way to delete a record obtained via the query() method. Specifically, we have an endpoint for user notifications. We want to, on page load, get all user notifications, use ng-repeat to loop over the results and display the notifications in the nav bar. When a user clicks a remove icon, the corresponding notification should be deleted. Here's the stripped down version of the code I currently have:
Js:
angular.module('myapp', ['ngResource']).factory('Notifications',function($resource){
return $resource('/apiv2/user/notifications/:id', {id:'#id'});
}).controller('NavigationController',['$scope','Notifications',function($scope, Notifications){
$scope.notifications = Notifications.query();
$scope.deleteNotification = function(notification){
notification.$delete();
};
}]);
HTML:
<ul>
<li ng-repeat="notification in notifications">
<i class="icon-remove" ng-click="deleteNotification(notification)"></i>
</li>
</ul>
With this code, when a user clicks on the remove icon, the individual notification object is passed to the deleteNotification method and is properly deleted from the backend via the api. Up until this point, everything works as intended. However, if I look at the $scope.notifications object after the fact, the notification that was just deleted remains with broken data:
{$promise:undefined, $resolved:true}
Ideally, I want this record wiped from the object returned via the .query() method to reflect its state on the back end, without having to do a new .query().
Any help would be appreciated! I apologize for vague descriptions and/or incomplete/innaccurate code, I typed this all from memory via my phones keyboard whilst out at dinner, so god knows if I missed something.
Better way of doing it: (see AngularJS ngResource delete event)
$scope.deleteNotification = function (index) {
$scope.notifications[index].$delete();
$scope.notifications.splice(index, 1);
}
and in your markup just do
ng-click="deleteNotification($index)"
There probably is a better way to do this as this throws a console error (but still works), but this is what I am doing:
$scope.notifications = Notifications.query();
$scope.deleteNotification = function(notification){
notification.$delete();
$scope.notifications = $scope.notifications.filter( function(n)
return (n != notification);
}); // filter everything but
};
if you use underscore there is a more beautiful way to write the remove thing.

Categories