I have a SAPUI5 table. What I want is to provide a text field, where user can enter time interval (like 3 mins), and the table will get refreshed automatically by itself after 3 mins. Moreover, if he provides new value (e.g. 8 mins), table will now get refreshed after 8 mins.
Can anyone give some ideas how can I achieve this? Thanks in advance.
As #sirion said, it depends a little on what table you have, how you want your scroll to behave and so on. I think it is inevitable that your table will "nudge" in some way or another if e.g. new lines get inserted in the table between refreshes or if some lies are deleted.
Nevertheless, I would say that the best option is to get the ListBinding from the table (as sirion said) and do a refresh on this binding:
onRefreshTriggered: function () {
this.byId("myTable").getBinding("items" /* or "rows" */).refresh();
}
It might also be an idea to refresh the element binding for each row, then you would surely not have problems with the scrolling (but you will have problems in case a row is deleted or added).
onRefreshTriggered: function () {
(this.byId("myTable").getItems() || []).forEach(function (oItem){
oItem.getElementBinding(/* model name */).refresh();
});
}
For doing the periodical triggering part, I would use the sap.ui.core.IntervalTrigger class. It is fairly easy to use:
// e.g. in onInit:
this._trigger = new IntervalTrigger(3 * 60 * 1000 /* initial interval */)
this._trigger.addListener(this.onRefreshTriggered, this);
// in a separate method, e.g. as a input field change event
onIntervalChange: function(oEvent) {
var iInterval = parseInt(oEvent.getSource().getValue(), 10);
this._trigger.setInterval(iInterval * 60 * 1000);
}
It depends on what kind of table you are using.
You can always just call oTable.getBinding("x").refresh(), with "x" being "items" or "rows". But that might lead to the table forgetting the scroll position if not everything is shown.
So the "real" solution would be to find out what slice of data is currently shown and then read exactly the same data from the back-end again. Using the read-method on the v2.ODataModel, the model stores the fresh data and updates the property bindings in the table as well.
window.setInterval can be used to call functions at a specific time interval.
window.clearInterval can be used to terminate it.
Attach a change handler to your text, clear any existing intervals and start a new interval with the new time.
<Input change="onInputIntervalChange" />
onInputIntervalChange: function(oEvent) {
var sIntervalInMinutes = oEvent.getParameter("newValue");
var iIntervalInMinutes = parseInt(sIntervalInMinutes);
var iIntervalInMillisec = iIntervalInMinutes * 60 * 1000;
window.clearInterval(this._intervalId);
this._intervalId = window.setInterval(function(){
// refresh your table
}.bind(this), iIntervalInMillisec);
}
Keep in mind that the first refresh will happen after the time interval has passed. If you want to refresh the table immediately when the user changes the value, call your refresh method directly before setInterval.
// Works
var counter = 0;
var myInterval = Meteor.setInterval(function(){
counter++;
var time = moment().hour(0).minute(0).second(counter).format('HH:mm:ss');
console.log(time);
}, 1000);
// Inside Helper - Does Not Work
Template.clockRunner.helpers({
start: function () {
var counter = 0;
var time = moment().hour(0).minute(0).second(counter).format('HH:mm:ss');
var myInterval = Meteor.setInterval(function(){
counter++
}, 1000);
return time;
},
})
The first version console logs the time in increments of 1 second. The Helper version displays "00:00:00" in the DOM, but does not increment, if I console log the time in helper it logs "00:00:00" every second.
I'm not sure if I'm misunderstanding the reactive nature of helpers or if I'm not seeing a minor mistake. Thanks in advance!
a helper is meant to provide data to a Blaze template; it won't get called unless invoked from a template.
that said, you should think of a helper as something that only provides data, it shouldn't "do anything." as a template renders, and as reactive data is processed, a helper may get called several times in unexpected ways.
i reckon you want your timer to be started in the onRendered() method; that is called once as a template is put on the screen. (there's a corresponding method that's called when the template is taken off the screen, so the timer can be stopped).
once your timer is started, you can write timer data to a reactive variable, and then a helper that returns a formatted version of that timer data. because it's in a reactive var, that will ensure your helper is re-invoked each time the timer ticks.
the last part is simply ensuring the Blaze template references the helper.
I'd like to animate a change to an individual ko.observable in the most MVVM/Knockoutesque way. I can handle the animation and view updates on my own:
function ViewModel() {
var self = this;
self.value = ko.observable("start value");
$("button").on("click", function () {
$("#text").animate(animateOutProperties,
{
complete: function () {
self.value($("#value").val());
$("#text").animate(animateInProperties);
}
});
});
}
The above works exactly as I want it to
However, the above does not take full advantage of two way data binding since I'm actually listening to an event and changes on the value itself. There's almost no point in using data-bind: text since I can just use jQuery to update the text at this point.
Using something like self.value.subscribe to listen to changes in the value would make more sense to me and I could use other bindings to update the value -- however, as far as I can tell there is no way to get both the old and new values at the same time.
I want to use something like beforeRemove and afterAdd, but those only work for adding and removing observableArray elements.
Is there a way to handle the above animation that fits better with the MVVM/two way data binding philosophy?
I have a function within my angular controller, I'd like this function to be run on document ready but I noticed that angular runs it as the dom is created.
function myController($scope)
{
$scope.init = function()
{
// I'd like to run this on document ready
}
$scope.init(); // doesn't work, loads my init before the page has completely loaded
}
Anyone know how I can go about this?
We can use the angular.element(document).ready() method to attach callbacks for when the document is ready. We can simply attach the callback in the controller like so:
angular.module('MyApp', [])
.controller('MyCtrl', [function() {
angular.element(document).ready(function () {
document.getElementById('msg').innerHTML = 'Hello';
});
}]);
http://jsfiddle.net/jgentes/stwyvq38/1/
See this post How to execute angular controller function on page load?
For fast lookup:
// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>
// in controller
$scope.init = function () {
// check if there is query in url
// and fire search in case its value is not empty
};
This way, You don't have to wait till document is ready.
Angular has several timepoints to start executing functions. If you seek for something like jQuery's
$(document).ready();
You may find this analog in angular to be very useful:
$scope.$watch('$viewContentLoaded', function(){
//do something
});
This one is helpful when you want to manipulate the DOM elements. It will start executing only after all te elements are loaded.
UPD: What is said above works when you want to change css properties. However, sometimes it doesn't work when you want to measure the element properties, such as width, height, etc. In this case you may want to try this:
$scope.$watch('$viewContentLoaded',
function() {
$timeout(function() {
//do something
},0);
});
Angular initializes automatically upon DOMContentLoaded event or when
the angular.js script is evaluated if at that time document.readyState
is set to 'complete'. At this point Angular looks for the ng-app
directive which designates your application root.
https://docs.angularjs.org/guide/bootstrap
This means that the controller code will run after the DOM is ready.
Thus it's just $scope.init().
The answer
$scope.$watch('$viewContentLoaded',
function() {
$timeout(function() {
//do something
},0);
});
is the only one that works in most scenarios I tested. In a sample page with 4 components all of which build HTML from a template, the order of events was
$document ready
$onInit
$postLink
(and these 3 were repeated 3 more times in the same order for the other 3 components)
$viewContentLoaded (repeated 3 more times)
$timeout execution (repeated 3 more times)
So a $document.ready() is useless in most cases since the DOM being constructed in angular may be nowhere near ready.
But more interesting, even after $viewContentLoaded fired, the element of interest still could not be found.
Only after the $timeout executed was it found. Note that even though the $timeout was a value of 0, nearly 200 milliseconds elapsed before it executed, indicating that this thread was held off for quite a while, presumably while the DOM had angular templates added on a main thread. The total time from the first $document.ready() to the last $timeout execution was nearly 500 milliseconds.
In one extraordinary case where the value of a component was set and then the text() value was changed later in the $timeout, the $timeout value had to be increased until it worked (even though the element could be found during the $timeout). Something async within the 3rd party component caused a value to take precedence over the text until sufficient time passed. Another possibility is $scope.$evalAsync, but was not tried.
I am still looking for that one event that tells me the DOM has completely settled down and can be manipulated so that all cases work. So far an arbitrary timeout value is necessary, meaning at best this is a kludge that may not work on a slow browser. I have not tried JQuery options like liveQuery and publish/subscribe which may work, but certainly aren't pure angular.
I had a similar situation where I needed to execute a controller function after the view was loaded and also after a particular 3rd-party component within the view was loaded, initialized, and had placed a reference to itself on $scope. What ended up working for me was to setup a watch on this scope property and firing my function only after it was initialized.
// $scope.myGrid property will be created by the grid itself
// The grid will have a loadedRows property once initialized
$scope.$watch('myGrid', function(newValue, oldValue) {
if (newValue && newValue.loadedRows && !oldValue) {
initializeAllTheGridThings();
}
});
The watcher is called a couple of times with undefined values. Then when the grid is created and has the expected property, the initialization function may be safely called. The first time the watcher is called with a non-undefined newValue, oldValue will still be undefined.
Here's my attempt inside of an outer controller using coffeescript. It works rather well. Please note that settings.screen.xs|sm|md|lg are static values defined in a non-uglified file I include with the app. The values are per the Bootstrap 3 official breakpoints for the eponymous media query sizes:
xs = settings.screen.xs // 480
sm = settings.screen.sm // 768
md = settings.screen.md // 992
lg = settings.screen.lg // 1200
doMediaQuery = () ->
w = angular.element($window).width()
$scope.xs = w < sm
$scope.sm = w >= sm and w < md
$scope.md = w >= md and w < lg
$scope.lg = w >= lg
$scope.media = if $scope.xs
"xs"
else if $scope.sm
"sm"
else if $scope.md
"md"
else
"lg"
$document.ready () -> doMediaQuery()
angular.element($window).bind 'resize', () -> doMediaQuery()
If you're getting something like getElementById call returns null, it's probably because the function is running, but the ID hasn't had time to load in the DOM.
Try using Will's answer (towards the top) with a delay. Example:
angular.module('MyApp', [])
.controller('MyCtrl', [function() {
$scope.sleep = (time) => {
return new Promise((resolve) => setTimeout(resolve, time));
};
angular.element(document).ready(function () {
$scope.sleep(500).then(() => {
//code to run here after the delay
});
});
}]);
Why not try with what angular docs mention https://docs.angularjs.org/api/ng/function/angular.element.
angular.element(callback)
I've used this inside my $onInit(){...} function.
var self = this;
angular.element(function () {
var target = document.getElementsByClassName('unitSortingModule');
target[0].addEventListener("touchstart", self.touchHandler, false);
...
});
This worked for me.
$scope.$on('$ViewData', function(event) {
//Your code.
});
Sorry if my title makes no sense but this is what I've gotten down to so far:
function createPostsArray(last) {
var postArr =
generate(
function () {
return {
postGroup: [],
visible: computed(
function (){ //return true if
//postGroup is empty
}
}, last)
.toArray();
posts(postArr);
}
basically, is postGroup[] is empty, I want to display a "loading" screen using CSS. When postGroup gets some elements, automatically visible should become false.
Later I will extend this computed function to also consider another observable, but this is tricky for me since visible is dependent on a specific postGroup, the one with which it is associated.
How can I do this? Does anyone know?
PS: Generate is a linq.js generate function and Computed is a ko.computed function
PS: What is a good reference to read on these concepts?
The knockout portion would look like this. postGroup would need to be an observableArray because you want to observe the array itself. Because visible is a ko.computed it will run its function whenever the number of elements in postGroup change.
The extend({ throttle: 100 }) isn't necessary, but if postGroup is populated via repetitive postGroup.push() calls it will aid performance. It causes a computed observable to delay re-evaluation until its dependencies have stopped changing for a specified period of time. http://knockoutjs.com/documentation/throttle-extender.html
return {
postGroup: ko.observableArray(),
visible: ko.computed(function() {
return postGroup().length > 0;
}).extend({ throttle: 100 })
}