I'm developing an app in angular dart and trying to animate a progress bar which shows progress of a file upload. I'm simply parsing the JSON from the file, and sending them off to a service with a _service.create(....) method.
I've put all of this code in an async method which is called when my submit button is clicked:
void uploadFile() async {
submitButton.attributes.addAll({ 'disabled': '' });
progress.value = 0;
FileList files = input.files;
if (files.isEmpty) {
_handleError("No file selected");
//handle error, probably a banner
return;
}
File file = files.item(0);
FileReader reader = new FileReader();
//reader.result
reader.onLoadEnd.listen((e) async {
Map map = json.decode(reader.result);
var combinations = map['combinations'];
progress.max = combinations.length;
int loopCount = 0;
combinations.forEach((e) async {
await _service.create(VJCombination.fromJSON(e)).then((_) {
combinationCount++;
progress.value++;
loopCount++;
if (loopCount == combinations.length) {
submitButton.attributes.remove('disabled');
}
});
});
isLoadSuccessful = true;
});
reader.onError.listen((evt) => print(evt));
reader.readAsText(file);
progress.value = 10;
}
I'm getting the progress and the submitButton elements with the #ViewChild annotations:
#ViewChild('progress')
ProgressElement progress;
#ViewChild('submit')
ButtonElement submitButton;
The code works. The progress bar starts off empty, and after the file is read and the service gets the data, the progress bar is full.
My issue is that the UI is only updated after all of the combinations have been sent to the _service. So it seemingly goes from empty to full in one frame.
This code
combinations.forEach((e) async {
does not make sense, because forEach does not care about the returned Future
Rather use
for(var e in combinations) {
Not sure if this fixes your problem, but such code definitely needs to be changed.
Related
Good Afternoon,
I have a question regarding the performance of the tabbed page and whether there is a more efficient way to load the tabs into the page, without any frame loss or process hang.
Regarding this issue, I have been having a few issues with TabbedPage in Xamarin.Forms. My project currently consists of a Listview with 104 components. When I click on one of the items it opens up a tabbed page consisting of 3 tabed pages. I start the tabbed page using the following code
int Clicked = 0;
public async Task CheckClick(Page data)
{
Clicked += 1;
if (Clicked == 1)
{
await Navigation.PushAsync(data);
List_View.SelectedItem = null;
}
}
public async void OnSelection(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
var Cell = e.SelectedItem as DataSource;
switch (Cell.ID)
{
case 0:
await CheckClick(new Function_One());
break;
}
}
The real problem comes after, whether I pre load the information ahead of time or do what ever, whenever I call Children.Add(data); the whole application hangs for 1 or 2 seconds and than allows the page to load. The code is as follows.
public class Function_One : TabbedPage
{
private async Task Test()
{
//
var data = new NewPageData("", "", "OP.png", "Persian.png", Description, Usage, Storage, Data);
var data2 = new NewMedicalPage(Medical_Info, Translation, startinfo);
var data3 = new NewNotePage("", 0);
data.Icon = "Info.png";
await Task.Delay(100);// Little await to load the page first before hang
Device.BeginInvokeOnMainThread(async () =
{
Children.Add(data);// HERE is the freeze
Children.Add(data2);// these
Children.Add(data3);// 3 (Adding of the Tabs)
});
}
public void UpdateData()
{
Task.Run(async () =
{
try
{
Description.Spans.Add(new Span
{
Text = "Data.... ",
FontSize = 18,
FontFamily = variables.fontFamily
});
Usage.Spans.Add(new Span
{
Text = "MoreData...",
FontSize = 18,
FontFamily = variables.fontFamily
});
Storage.Spans.Add(new Span
{
Text = "LastData...",
FontSize = 18,
FontFamily = variables.fontFamily
});
await Test();
}
catch { }
}).ConfigureAwait(false);
}
public Function_One()
{
UpdateData();
BarBackgroundColor = MainColor;
BarTextColor = Color.WhiteSmoke;
Title = "Page";
}
}
NOTE: (This Project Is Completely An Offline Project, No Internet Needed)
If anyone could explain to me a better method for loading the data while keeping the application flowing and smooth, that would be appreciated. The data does not have to load all at once, just as long as the page opens as soon as its clicked!
On the App.xaml.cs I have the following code
private async void OnCommandsRequested(SettingsPane settingsPane, SettingsPaneCommandsRequestedEventArgs e)
{
var loader = ResourceLoader.GetForCurrentView();
var generalCommand = new SettingsCommand("General Settings", "General Settings", handler =>
{
var generalSettings = new GeneralSettingsFlyout();
generalSettings.Show();
});
e.Request.ApplicationCommands.Add(generalCommand);
object data;
IAuthService _authService = new AuthService();
if (Global.UserId == 0)
data = await _authService.GetSettingValueBySettingName(DatabaseType.GeneralDb, ApplicationConstants.GeneralDbSettingNames.ShowSupportInfo);
else
data = await _authService.GetSettingValueBySettingName(DatabaseType.UserDb, ApplicationConstants.UserDbSettingNames.ShowSupportInfo);
if (data != null && data.ToString().Equals("1"))
{
var supportCommand = new SettingsCommand("Support", "Support", handler =>
{
var supportPane = new SupportFlyout();
supportPane.Show();
});
e.Request.ApplicationCommands.Add(supportCommand);
}
var aboutCommand = new SettingsCommand("About", loader.GetString("Settings_OptionLabels_About"), handler =>
{
var aboutPane = new About();
aboutPane.Show();
});
e.Request.ApplicationCommands.Add(aboutCommand);
}
This code adds the setting "General Settings" but neither "Support" or "About" commands. Can anyone advice what's wrong with this code?
Instead of querying the commands from your service when they are requested you'll need to query them ahead of time and then add the already known commands.
You cannot use await in OnCommandsRequested.
A method returns when it gets to the first await, so only commands added to the request before the await will be used.
Since the SettingsPaneCommandsRequestedEventArgs doesn't provide a deferral there is no way to tell the requester to wait for internal async calls to complete.
Note also that SettingsPane is deprecated and not recommended for new app development for Windows 10.
In my MVC project I generate an array of images and store the array as a session variable, I animate the images using slidebar and by detecting mouse movement while mouse button is down by calculating the distance between the first click and x position while the mouse is moving on a canvas.
In the controller I use:
public ActionResult Animate(int slice = 0, int udm = 0)
{
FileContentResult data;
Image objImage = null;
Bitmap im = null;
try
{
im = MySession.Current.imageArray[slice];
....
MySession.Current.image = im;
}
else
{
return RedirectToAction("Index",new {.... });
}
}
catch { }
return null;
}
and
public ActionResult ImageOut(int udm = 0)
{
FileContentResult data;
Image objImage = null;
Bitmap im = null;
im = MySession.Current.image;
...
objImage = im.Bitmap(outputSize, PixelFormat.Format24bppRgb, m);
MemoryStream ms1 = new MemoryStream();
using (var memStream = new MemoryStream())
{
objImage.Save(memStream, ImageFormat.Png);
data = this.File(memStream.GetBuffer(), "image/png");
}
objImage.Dispose();
return data;
}
From the view I use Ajax:
$.ajax({
url: '/Home/Animate',
type: 'POST',
async: false,
data: {
slice: ((lastX - firstX) + nSlice),
udm: ++udm
},
success: function(data) {
if (data.udm) {
nSlice = (data.slice);
image.src = '/Home/ImageOut?' + $.param({
udm: data.udm
});
}
},
error: function() {
}
});
I have two problems, first it takes time to update the view and skips a number of images, the second is it open many threads and if a number of users accessing the same page it slows down. I thought of using async but I am still using c# 4 and this may requires lots of changes to my code. I was reading about SignalR, my question is can this be done (providing I just update the user screen not all users) or is there a better solution.
The sequence of events I would like to achieve is:
Ajax send to the first action a request or generate the first image and wait
When the image is generated, Ajax receive success, then display the image on the screen using the second action
Then the first action generate the second image
The challenge I see is the first image keep generating the images without waiting, so my question is how I make the first action wait, and how to send to it a message to generate the following image.
I just installed VS2012 c#5, is there any example that can help me!! Would appreciate your suggestions, thanks in advance.
Using TPL, you could try this (taking your code above), the same can be applied for the animate method:
public ActionResult ImageOut(int udm = 0)
{
FileContentResult data = null;
Image objImage = null;
Task.Run(() =>
{
Bitmap im = MySession.Current.dicomImage;
objImage = im.Bitmap(outputSize, PixelFormat.Format24bppRgb, m);
using (var memStream = new MemoryStream())
{
objImage.Save(memStream, ImageFormat.Png);
data = this.File(memStream.GetBuffer(), "image/png");
}
});
objImage.Dispose();
return data;
}
Task.Run is just shorthand for Task.Factory.StartNew
Rather than changing my program to use TPL because of the learning curve; I just added async: false to my ajax; this helped to delay the refresh of the screen. Not the best approach but helped a bit.
I have an interface that calls a script for spreadsheet creation using data taken from other spreadsheet. I want the interface to update its labels at runtime in order to give visual feedback to the user and let him know the script is running and it's not stuck. When I try to update the label I put in the interface, it doesn't update the first time, but updates correctly after myFunction() reaches its end. Which means I can see the message "Creation Completed", but the message "Creating file..." is never shown. Also, the button buttonCompile is never disabled so it seems that the instructions before myFunction() are not executed at all. How can I get the labels updated and the button disabled before myFunction() starts executing? (I already double-checked variable references)
function doGet() {
var app = UiApp.createApplication();
app.add(app.loadComponent("File creation"));
var buttonCreate = app.getElementById('createBtn');
var handlerCrea = app.createServerHandler('createClickHandler');
buttonCreate.addClickHandler(handlerCreate);
return app;
}
function createClickHandler(e) {
var app = UiApp.getActiveApplication();
var label = app.getElementById('createLbl');
label.setText("Creating file...");
var buttonCompile = app.getElementById('compileBtn');
buttonCompile.setEnabled(false);
myFunction();
label.setText("Creation completed.");
buttonCompile.setEnabled(true);
app.close();
return app;
}
The cause of this behavior is that the GUI is updated only after leaving a handler. A workaround is to use two handlers. The 1st one sets the label text to Creating file... and disables the button, the 2nd one executes the myFunction function, changes the text to Creation completed, and eanbles the button. Here is an example. It disables/enables the button and the worker handler simply waits 5 seconds.
function doGet(e) {
var app = UiApp.createApplication();
var container = app.createHorizontalPanel().setId('container');
var btnPerformance = app.createButton("Performance Demo").setId('btnPerformance');
var handlerPerformance = app.createServerHandler('onBtnPerformanceClick');
var handlerWait = app.createServerHandler('onWait');
btnPerformance.addClickHandler(handlerPerformance);
btnPerformance.addClickHandler(handlerWait);
container.add(btnPerformance);
app.add(container);
return app;
}
function enableControls(enable) {
var lstControls = [ 'btnPerformance' ];
var app = UiApp.getActiveApplication();
for (var i = 0; i < lstControls.length; i++) {
var ctl = app.getElementById(lstControls[i]);
ctl.setEnabled(enable);
}
}
function onWait(e) {
enableControls(false);
return UiApp.getActiveApplication();
}
function onBtnPerformanceClick(e) {
Utilities.sleep(5000);
enableControls(true);
return UiApp.getActiveApplication();
}
for (var i = 0; i < 5; ++i) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
xhr.open('GET', '/Test/LongOperation?p=' + new Date());
xhr.send('');
}
This is only a demo (not live code) but it illustrates the core problem.
LongOperation is a method that returns a result after 10 seconds.
Questions:
Why does IE8 (and maybe other IEs) hang when the user tries to navigate away from page right after the above code snippet has been executed? FireFox/Safari cancel these requests and allow navigation to another page. If you replace 'i < 5' with 'i < 4' then IE would not hang.
How to work around this ugly IE behavior? Users are very upset when their browser suddenly hangs.
Most browsers have an inbuilt limit of 4 connections to any given server. One way to work around this "problem" might be to use a different hostname for out of band XML requests - your user requests will go to the main hosts, while the AJAX requests can go to the second server.
My answer to my question. I abort all not completed xhr objects in window.onbeforeunload. At least this solution works for me. I slightly override $.ajax() method behavior:
;(function($) {
var rq = [];
var ajax = $.ajax;
$.ajax = function(settings) {
// override complete() operation
var complete = settings.complete;
settings.complete = function(xhr) {
if (xhr) {
// xhr may be undefined, for example when downloading JavaScript
for (var i = 0, len = rq.length; i < len; ++i) {
if (rq[i] == xhr) {
// drop completed xhr from list
rq.splice(i, 1);
break;
}
}
}
// execute base
if (complete) {
complete.apply(this, arguments)
}
}
var r = ajax.apply(this, arguments);
if (r) {
// r may be undefined, for example when downloading JavaScript
rq.push(r);
}
return r;
};
// 'kill' all pending xhrs
$(window).bind('beforeunload', function() {
$.each(rq, function(i, xhr) {
try {
xhr.abort();
} catch(e) {
$debug.fail('failed to abort xhr');
}
});
rq = [];
});
})(jQuery);
$debug - my utility class
Try running them asynchronously and then triggering the next http request when the each completes. I suspect that the xmlhttp request is blocking the UI thread of IE whereas the implementations of that on other browsers is a little more graceful.
Hopefully that will give you a work-around for question 2 but I can only guess at the true reason for question 1, it could just be a bug.