I am using angularfire 0.5.0 to set a scope variable called tokenRate in one of my controllers and displaying the value of tokenRate in the corresponding HTML file like so:
Controller:
function TokensCtrl($firebase, $scope) {
$scope.tokenRate = $firebase(new Firebase("https://TEST_URL.firebaseio.com/token_rate"));
}
HTML:
<h1>Token rate is {{ tokenRate }}</h1>
I would expect the value to be displayed the first time and automatically update whenever "token_rate" is changed on the remote firebase. Instead, the tokenRate seems to be undefined initially and does not update when I change "token_rate" on the remote firebase.
The data sync is happening because I am able to receive "change" events from the remote firebase. I am guessing angularjs somehow doesn't know about the object change because putting a $watch on "tokenRate" doesn't trigger anything. I can get it to display the behavior I want by doing:
var value = $firebase(new Firebase("https://TEST_URL.firebaseio.com/token_rate"));
value.$on("change", function() {
$scope.tokenRate = value.$value;
});
But this seems ridiculous to do every time. I don't think I am understanding something here. What is the proper way to set the $scope.tokenRate variable so that it displays the first time and syncs automatically?
Related
I've tried to prepare data from an OData source to show it in a bar graph in my fiori app. For this, I setup the OData model in the manifest.json. A test with a list, simply using
items="{path : 'modelname>/dataset'}
works fine and shows the content.
To prepare data for a diagram (VizFrame), I used the onInit() function in the controller of the view (mvc:XMLView). The data preparation is similar to the one discussed in question.
At first I obtain the ODataModel:
var oODataModel = this.getOwnerComponent().getModel("modelname");
Next I do the binding:
var oBindings = oODataModel.bindList("/dataset");
Unfortunately, the oBindings().getContexts() array is always empty, and also oBindings.getLength() is zero. As a consequence, the VizFrame shows only "No Data".
May it be that the data model is not fully loaded during the onInit() function, or do I misunderstand the way to access data?
Thanks in advance
Update
I temporary solved the problem by using the automatically created bind from the view displaying the data as list. I grep the "dataReceived" event from the binding getView().byId("myList").getBindings("items") and do my calculation there. The model for the diagram (since it is used in a different view) is created in the Component.js, and registered in the Core sap.ui.getCore().setModel("graphModel").
I think this solution is dirty, because the graph data depends on the list data from a different view, which causes problems, e.g. when you use a growing list (because the data in the binding gets updated and a different range is selected from the odata model).
Any suggestions, how I can get the odata model entries without depending on a different list?
The following image outlines the lifecycle of your UI5 application.
Important are the steps which are highlighted with a red circle. Basically, in your onInit you don't have full access to your model via this.getView().getModel().
That's probably why you tried using this.getOwnerComponent().getModel(). This gives you access to the model, but it's not bound to the view yet so you don't get any contexts.
Similarly metadataLoaded() returns a Promise that is fullfilled a little too early: Right after the metadata has been loaded, which might be before any view binding has been done.
What I usually do is
use onBeforeRendering
This is the lifecycle hook that gets called right after onInit. The view and its models exist, but they are not yet shown to the user. Good possibility to do stuff with your model.
use onRouteMatched
This is not really a lifecycle hook but an event handler which can be bound to the router object of your app. Since you define the event handler in your onInit it will be called later (but not too late) and you can then do your desired stuff. This obviously works only if you've set up routing.
You'll have to wait until the models metadata has been loaded. Try this:
onInit: function() {
var oBindings;
var oODataModel = this.getComponent().getModel("modelname");
oODataModel.metadataLoaded().then(function() {
oBindings = oODataModel.bindList("/dataset");
}.bind(this));
},
May it be that the data model is not fully loaded during the onInit()
function, or do I misunderstand the way to access data?
You could test if your model is fully loaded by console log it before you do the list binding
console.log(oODataModel);
var oBindings = oODataModel.bindList("/dataset");
If your model contains no data, then that's the problem.
My basic misunderstanding was to force the use of the bindings. This seems to work only with UI elements, which organize the data handling. I switched to
oODataModel.read("/dataset", {success: function(oEvent) {
// do all my calculations on the oEvent.results array
// write result into graphModel
}
});
This whole calculation is in a function attached to the requestSent event of the graphModel, which is set as model for the VizFrame in the onBeforeRendering part of the view/controller.
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>
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();
});
I'm not sure if i have completely wrapped my head around this idea - but I'll try my best to clearly describe what I am trying to do here.
I have a factory that changes and parses a URL for me, so I can pass params into a controller for use (that were stored in the url). This is sort of so I can save a state for the user and they can share it via copy'ing of a URL (send it to their friends or bookmark it or w/e).
I am trying to set up a factory (or service) that listens for locationChangeSuccess - so that if the user mofies the url and presses enter, it will refresh the scopes in the controllers. So here is what I have:
.factory("urlFactory", function($location, freshUrl, StateString){
//request to change new url
requestObj.requestState = function(moduleName, stateName, startVar){
}
//request item from url, via your module and state name
requestObj.parseState = function(moduleName, stateName){
}
I dropped the center out (if it is needed im happy to link), but those just get and set the url for me.
So in the controllers I do something like
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
$scope.mod2m4 = urlFactory.parseState("module2", "mod4");
So when they land on the page, they pull their state. This works great. However, now i'm trying to solve some edge case scenarios where maybe the user modifies the url.
So I can latch onto that even pretty easily with
.factory("urlWatcher", function($location, $scope){
var urlWatcher = {};
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
return urlWatcher
});
However, where I am struggling is trying to determine a way where when this fires, it would connect the new value to the scope in the controller. It was suggested to me that a callback of some sort in the parse (set) function, but I am struggling with how to approach that. It would be super cool if I could set a way for this factory/service to re send the new value when it changes to the right place. Callback sounds good, however I don't know how to config this correct.
The easiest route would be to just do an
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
In each controller and manually bind to each scope, but I am trying to make this as modular as possible (and thats also a ton of watchers on the locationchangesuccess). would be fantastic if I could figuire out a clean way to set the service/factory to listen once, and on change find the right module/controller and change the value.
I can't seem to think a clear route, so I would be very greatful for any insight to this issue. Thank you very much for reading!
If what you want is a publish/subscribe architecture, where publications are global and subscriptions have the same lifecycles as Angular scopes... then Angular events are what you're looking for. There's no point setting up an ad hoc communication system with callbacks and whatnut, that would just be partially reinventing events.
However, if you want to make the semantics more obvious / add flexibility, you can listen once to $locationChangeSuccess in a service and broadcast a custom event.
$rootScope.$on("$locationChangeSuccess", function (event) {
$rootScope.$broadcast('myCustomeEvent', {message: "Guys, time to refresh!"});
});
Then listen to this event in each of the scopes where it is relevant.
$scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
If setting up the listening gets repetitive, by all means, factor it out in a function, which you can for example put in a service:
myApp.factory('someFactory', [function () {
return {
listenToLogAsdsa: function (scope) {
scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
}
};
}]);
Then all you have to write in your controller is:
someFactory.listenToLogAsdsa($scope);
You can assign a variable in the scope to an object in the factory, that way it's bound to a reference instead of a value. Then, in your HTML you bind the reference to the DOM. urlFactory.parseState() should then save the result to said object, and return the key where it was saved.
For example:
In urlFactory:
requestObj.parseState = function(moduleName, stateName){
var key = moduleName+stateName;
this.urlContainer[key] = "www.example.com";
return key;
}
In the controller:
$scope.urls = urlFactory.urlContainer;
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
In your HTML:
{{urls[mod2m3]}}
This way, "urls" is bound to a reference, which angular watches for changes, and whenever you change urls[mod2m3], it will affect the DOM.
You can also just react to changes in the scope variables by watching them:
$scope.$watch('urls', function() {
//do something
});
NOTE: Since this is an object, you might need to use $watchCollection instead of $watch.
I have an http request that grabs some data and applies it to a $scope variable:
$http.post("/api/division", 1).success(function(data){
$scope.division = data;
});
Now in my HTML I iterate through that data and display various bits of information, for example:
<div data-ng-repeat="player in division">
<div>{{player.name}}</div>
<div>{{player.number}}</div>
<div>{{player.score}}</div>
</div>
This works great. Now, I have a link that triggers a function to get a new set of data and bind it to the same $scope variable so the frontend will update with new information.
Get Division
$scope.getDivision = function(){
$http.post("/api/division", 2).success(function(data){
$scope.division = data;
});
}
Now, the front end does update with the new information, but there seems to be a lag of 5-10 seconds between the data successfully coming back and the HTML updating. So the problem isn't with the speed of the server. Is it something to do with needing to $emit or $broadcast the change to the variable? How can I get rid of the lag?