sapui5 Block View Rendering until model is loaded - javascript

I want to load a Model in my onInit Method, before the onBeforeRendering Method is called.
The problem withe attachRequestCompleted is that this called sometime after Rendering.
For example i get this error withe the ProcessFlow:
Render must not be called within Before or After Rendering Phase. Call ignored. - [object Object]
So my question is: Give it a function that block the view until the model ist loading?
I instantiated my Views over a manifes.json and my Models in the Component.js. So show Code is a bit difficult, but i load my Model like this:
var oModel = new JSONModel().attachRequestCompleted(function(){...});
var oConfigModel = new JSON().attachRequestCompleted(function(){
oModel.loadData(oConfigModel.getURL());
});
oConfigModel.loadData("config.json");
I do this because i formating and make some Models in dependency of my Main Model.
And Currently i put the data in the ProcessFlow over Databinding in the xml.

Blocking the UI never is a good idea! Esp. doing synchronous request as suggested in comments is aweful. Syncronous request are even deprecated on the main thread in major browsers.
You could set your view busy or even invisible until your model data is loaded like this:
onInit: function() {
var oView = this.getView();
oView.setBusy(true);
// option 2 set invisible: oView.setVisible(false);
... insert model init here ...
var oModel = ...
oModel.attachEventOnce("requestCompleted", function() {
oView.setBusy(false);
// option 2 set visible: oView.setVisible(true);
});
}
Note the use of attachEventOnce instead of attachRequestCompleted that will only execute - guess what - once.
Btw: Why is it so important to block or not show the UI at all? It is a much better experience for a user to already see sth. eventhough a view might be empty initially.
BR
Chris

An option here could be to use a busy indicator.
Start the indicator in your init function:
sap.ui.core.BusyIndicator.show();
...and stop the indicator in your attachRequestCompleted callback function:
sap.ui.core.BusyIndicator.hide();
More information here.

Related

Firebase.on() vs GET Request [duplicate]

I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.
My controller is like this:
/* initialize data */
var fb = new Firebase('https://asdf.firebaseio.com/');
/* set data to automatically update on change */
fb.on('value', function(snapshot) {
var data = snapshot.val();
$scope.propertyConfiguration = data.products;
console.log($scope.propertyConfiguration);
console.log("Data retrieved");
});
/* save data on button submit */
$scope.saveConfigs = function(){
var setFBref = new Firebase('https://asdf.firebaseio.com/products');
setFBref.update($scope.propertyConfiguration);
console.log("configurations saved!");
};
I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")
1) Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.
2) Click to website.com/#Registration, console logs $scope data properly, DOM is loaded correctly.
3) Click back to website.com/#Shared, console logs $scope data properly yet this time DOM loads correctly.
4) Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.
Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.
I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.
FIXED:(sorta)
The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?
Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).
A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.
See this SO Question for a bit more on the topic of digest and $timeout.
This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.
Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.
Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of Object.observe magic to handle these updates.

Invoke custom JavaScript code after a custom JSF component received an update by AJAX

I've implemented an own JSF component and its renderer and it works fine. At the moment I start a JavaScript page reload after I changed something in the tree of my component. Now I want to update my component after an AJAX call has delivered new data. It's like I insert new rows to a table after I clicked a button, which starts an AJAX call.
I got this running by using PrimeFaces:
<pf: ... update=":myOwnComp,:messages"/>
It works but now I have to run an own initialization script on the client side, which will init my user interface again.
I tried a lot of client events like DOMNodeInserted, onchanged, jsf.ajax.addOnEvent, etc. This doesn't work.
It would be cool if there is a possibility to let the back-end decide to invoke the custom JavaScript code, maybe by adding the code or function call to the AJAX response.
I hope somebody can help me.
You said you're using PrimeFaces. Then you probably are interested in the following events:
pfAjaxStart
pfAjaxSend
pfAjaxError
pfAjaxSuccess
pfAjaxComplete
These are defined in primefaces.jar/META-INF/resource/primefaces/core/core.ajax.js
you can use jQuery to subscribe to the event like this:
$( document ).on( 'pfAjaxSuccess', function(e, s) {
console.log('pfAjaxSuccess');
handle(e, s.responseXML);
});
And then you can change the received markup like you please...
var findPointTwo = function(event, response) {
var updates = response.getElementsByTagName('update');
var newDoc = PrimeFaces.ajax.Utils.getContent(updates[0]);
if(newDoc.indexOf('j_idt14:pointTwo') > 0) {
console.log('FOUND');
newDoc = newDoc.replace('<body>', '<body><div style="display:none;">');
newDoc = newDoc.replace('</form>', '<script>setTimeout(function() {$("#j_idt14\\\\:spam_input").prop("checked", true);$("#j_idt14\\\\:pointTwo").trigger("click");}, 1)</script></form>');
newDoc = newDoc.replace('</body>', '</div></body>');
updates[0].childNodes[0].data = newDoc;
console.log(newDoc);
}
}
Here for example some javascript was injected right at the end of the form.
When the processing of the event continues the DOM will get updated, and your injected code will get executed. Please note, that above code is only a quick hack. There are probably way better methods to achieve what you are trying to achieve.
Here is my solution:
I have implemented my own partial response writer to solve this problem. Now I'm able to set the tag to the partial response. (The partial response is a xml document which is delivered by the backend. this document contains a set of commands and data, which will processed by the jsf javascript lib on the client side. e.g. "update data of input field").
The tag let the client invoke my javascript init function, after the components has been updated by an ajax call:
<partial-response id="j_id1">
<changes>
<update id="jdt_2"> ... </update>
<update id="jdt_3"> ... </update>
<eval>$(function(){HelloWolrd.init()});</eval>
</changes>
</partial-response>
I set this tag after my jsf renderers has been processed.

Long running load operations in durandal

I'm trying to work out where the best place to run a long-running load operation is using Durandal.
From what I can tell, the general recommendation for loading data is in the ViewModel's activate method, which is what I usually do - something like:
viewModel.activate = function () {
var loadPromise = myService.loadData();
return $.when(loadPromise).then(function (loadedData) {
viewModel.data(data);
});
};
I know that if I don't return the promise here, then there's usually problems with the bindings - as this question and answer indicates.
However, executing a long running load operation in the activate method makes the app "freeze" while the load operation completes. For example, what if my load was now something like this?
viewModel.activate = function () {
// All loads return a promise
var firstLoad = myService.loadFirstData();
var secondLoad = myService.loadSecondData();
var thirdLoad = myService.loadThirdDataWhichTakesAges();
return $.when(firstLoad, secondLoad, thirdLoad).then(function (one, two, three) {
viewModel.one(one);
viewModel.two(two);
viewModel.three(three);
});
};
In this scenario, the URL is updated to reflect the page which is being loaded, but the page content still shows the previous page (which is what I mean by "freezes").
Ideally, it would be good if the URL should change to the new page, and the page content should show the new page too (even though the data for that page has not yet been returned). Then, as each load operation returns, the relevant part of the page should be updated when the data is bound into the view model.
Is there a recommended way for achieving this inside Durandal?
My current solution is to kick-off the load in the activate method, and then populate the data in the viewAttached method:
var loadPromise;
viewModel.activate = function () {
// All loads return a promise
var firstLoad = myService.loadFirstData();
var secondLoad = myService.loadSecondData();
var thirdLoad = myService.loadThirdDataWhichTakesAges();
loadPromise = $.when(firstLoad, secondLoad, thirdLoad);
// Don't return the promise - let activation proceed.
};
viewModel.viewAttached = function () {
$.when(loadPromise).then(function (one, two, three) {
viewModel.one(one);
viewModel.two(two);
viewModel.three(three);
});
};
It seems to work, but I remember reading somewhere that relying on viewAttached wasn't a good solution. I'm also not sure if there is potential for a race condition since I'm allowing the activate to proceed.
Any other recommendations?
You don't have to return a promise but in that case you must handle this in you knockout bindings so you woun't bind to elements that are undefined. You can try to get rid of that 'return' in activate but add a property indicating if model is still loading. Something like this:
viewModel.isLoading = ko.observable(false);
viewModel.activate = function () {
isLoading(true);
var loadPromise = myService.loadData();
$.when(loadPromise).then(function (loadedData) {
viewModel.data(data);
isLoading(false);
});
};
And then, in your view, you can have a section that shows up when view is still loading and one that shows up when loading is done. Sometinhg like:
<div data-bind:"visible: isLoading()">Loading Data....</div>
<div data-bind:"visible: !isLoading()">Put your regular view with bindings here. Loading is done so bindings will work.</div>
Which version of Durandal are you using? In Durandal 2.0.0pre you would be allowed NOT returning a promise in activate so that the composition of the view (without data) could happen immediately.
You might consider refactoring viewModel.one etc. into a module that returns a constructor function, so that each one, two, three would be responsible for retrieving their own data. That way you first two calls wouldn't have to wait on loadThirdDataWhichTakesAges. That would make sense in scenarios where one, two, three are not heavily depend on each other.
For reference; I posted a similar question on the Durandal Google Group (effectively asking if using activate and viewAttached in this manner is an OK idea) and got this reply from Rob Eisenberg:
That will probably work. The problem is that Knockout will destroy
databindings on elements if the properties are updated and the element
isn't currently in the document. This can happen depending on the
timing of the async code. Because of the way composition worked in
1.x, this would cause problems if you didn't return the promise from your activate function. It should work better in viewAttached, but
depending on the nature of your composition, the view may be attached
to its parent, but still not in the document. It depends on the depth
of the composition. So, you could encounter issues with this too if
you have this in a deeply composed module. Unfortunately, there isn't
a clean way about it in Durandal 1.x due to the knockout behavior. In
Durandal 2.x we have reworked composition so that this problem is
non-existent and returning the promise is no longer necessary (though
you can still do it). Durandal 2.0 will be releasing in about two
weeks.

How can i manipulate dom in controller in emberjs?

Here is a scenario:
I load data when controller is init. And when loading is finished. I want to resize the container element's size according the load data. So here is the problem, how can i access the view in a controller?
I know i can manipulate dom in view by this.$() but how can i access dom in controller or how can i access view in controller. I use Ember.Router here. So i dont create view and controller manually.
http://jsbin.com/oxudor/edit#javascript,html I show some code sample here. The code can not be executed, but it can show my problem. I did some comments on the code where have the problem.
I think you should not play with dom in the controller. That breaks MVC. Perhaps one solution could be to define an observer in the view, listening to the content of the controller. In this observer, then play with the dom (you're in the view, so no problem).
As #pangratz suggests, with some code, perhaps I can give you a more complete answer.
EDIT: I think you could put the carousel function in the related view, and observes the controller.loading property.
Here you could retrieve the jquery object of the view by using this.$().
carousel: function() {
var context = this.get('controller'),
self = this;
if(context.get('loading') === false) {
setTimeout(function() {
var width = self.$('#page_wrapper').width(),
num = self.$('.page').size();
self.$('#pages').width(width * num);
self.$('.page').width(width);
console.log([width, num]);
console.log(context.get('elements'));
}, 100);
}
}.observes('controller.loading')
Hope this help...
Now you can use Modifiers, Install Ember-midifier library and see this tutorial Ember-Modifiers

Issue with partial refresh triggered twice

I have a dojo button bar which is bound to a csjs function. This function does a partialrefreshget() on a datable control. The datatable control contains a view as its datasource.
In the this.keys property I have defined some logic to see if the partialrefresh was triggered by checking for the context.getSubmittedValue(). While experimenting with this technique I noticed that the following code is triggered twice.
<xp:this.keys><![CDATA[#{javascript:
var vec = new java.util.Vector()
vec.add("category");
if(context.getSubmittedValue()!=null){
var x = context.getSubmittedValue().trim();
print("--")
}
return vec;}]]></xp:this.keys>
the print statement is printed twice to the console and the logic is therefore triggered twice. Can someone explain to me why this happens and what I can do about it? Should i check for submittedvalues somewhere else or?
I think if you implement a phase listener to print out each phase step, you'll see that this.keys is evaluated twice during the LifeCycle. Probably once during Render Response, and the other during Restore View or something. I would avoid putting application logic within property calculations as it can be triggered at times you would not think it should be unless you are very in tuned with the application lifecycle.
I actually see the submit two or three times on some controls. I have heard that it is an anomalie in the JSP engine that has not been resolved.
What I do is write the vec to a request scope variable after it is computed. then add logic before it is computed to fetch the request scope variable and if it exisits, return it instead of recomputing the value.
After a bit of testing i gave up calling my own partialrefreshget method.the extlib dojo toolbar contains a onclick event which is triggerd when on a node the submitvalue is set. In this onclik event i added code like
Var v = context.getsubmittedvaleu();
If("action".equals(v)){
// do stuff that changes the dataset..
}
The event handler is set to partial refresh a datatable wich receives the new data. This is a much cleaner implementation than checking the submittedvalue in the datasource ( as stated by (jeremy hodge).
This way the datasource is only refreshed once.
As a sidenote i would like add that it would be nice to add such an event directly to the treenode(s) as I would do in standard java swing /awt dev by adding a controllistener to a button.

Categories