Merge pull request #1032 from NativeScript/loaded-twice

Resolved issues #789, #1021, #1031.
This commit is contained in:
Rossen Hristov
2015-11-04 09:27:09 +02:00
12 changed files with 179 additions and 34 deletions

View File

@ -3,7 +3,8 @@ application.mainModule = "main-page";
import trace = require("trace");
trace.enable();
trace.setCategories(trace.categories.concat(
trace.categories.Layout,
"LayoutRootView.iOS"
trace.categories.Navigation
//trace.categories.Layout,
//"LayoutRootView.iOS"
));
application.start();

View File

@ -3,7 +3,6 @@ import pages = require("ui/page");
import textField = require("ui/text-field");
import frame = require("ui/frame");
var context: any;
var closeCallback: Function;
var page: pages.Page;
@ -11,8 +10,8 @@ var usernameTextField: textField.TextField;
var passwordTextField: textField.TextField;
export function onShownModally(args: pages.ShownModallyData) {
console.log("login-page.onShownModally, context: " + args.context);
context = args.context;
console.log(">>> login-page.onShownModally, context: " + args.context);
closeCallback = args.closeCallback;
var modalPage = <pages.Page>args.object;
@ -21,18 +20,40 @@ export function onShownModally(args: pages.ShownModallyData) {
}
}
export function onNavigatingTo(args: observable.EventData) {
console.log(">>> login-page.onNavigatingTo");
}
export function onLoaded(args: observable.EventData) {
console.log("login-page.onLoaded");
console.log(">>> login-page.onLoaded");
page = <pages.Page>args.object;
usernameTextField = page.getViewById<textField.TextField>("username");
passwordTextField = page.getViewById<textField.TextField>("password");
}
export function onNavigatedTo(args: pages.NavigatedData) {
console.log(">>> login-page.onNavigatedTo, context: " + args.context);
}
export function onNavigatingFrom(args: observable.EventData) {
console.log(">>> login-page.onNavigatingFrom");
}
export function onNavigatedFrom(args: observable.EventData) {
console.log(">>> login-page.onNavigatedFrom");
}
export function onUnloaded() {
console.log("login-page.onUnloaded");
console.log(">>> login-page.onUnloaded");
}
export function onLoginButtonTap() {
console.log("login-page.onLoginButtonTap");
console.log(">>> login-page.onLoginButtonTap");
if (closeCallback) {
closeCallback(usernameTextField.text, passwordTextField.text);
}
else {
frame.topmost().goBack();
}
}

View File

@ -1,4 +1,12 @@
<Page xmlns="http://www.nativescript.org/tns.xsd" shownModally="onShownModally" loaded="onLoaded" unloaded="onUnloaded" backgroundColor="Red">
<Page xmlns="http://www.nativescript.org/tns.xsd"
shownModally="onShownModally"
navigatingTo="onNavigatingTo"
loaded="onLoaded"
navigatedTo="onNavigatedTo"
navigatingFrom="onNavigatingFrom"
navigatedFrom="onNavigatedFrom"
unloaded="onUnloaded"
backgroundColor="Red">
<StackLayout backgroundColor="PaleGreen">
<TextField hint="username" id="username" text="username"/>
<TextField hint="password" id="password" text="password" secure="true"/>

View File

@ -6,8 +6,14 @@ import frame = require("ui/frame");
var page: pages.Page;
var label: labelModule.Label;
export function onNavigatingTo(args: observable.EventData) {
console.log(">>> main-page.onNavigatingTo");
//console.trace();
}
export function onLoaded(args: observable.EventData) {
console.log("main-page.onLoaded");
console.log(">>> main-page.onLoaded");
//console.trace();
if (args.object !== frame.topmost().currentPage) {
throw new Error("args.object must equal frame.topmost().currentPage on page.loaded");
}
@ -19,12 +25,38 @@ export function onLoaded(args: observable.EventData) {
}
export function onNavigatedTo(args: observable.EventData) {
console.log("main-page.onNavigatedTo");
console.log(">>> main-page.onNavigatedTo");
//console.trace();
}
export function onNavigatingFrom(args: observable.EventData) {
console.log(">>> main-page.onNavigatingFrom");
}
export function onNavigatedFrom(args: observable.EventData) {
console.log(">>> main-page.onNavigatedFrom");
}
export function onUnloaded(args: observable.EventData) {
console.log(">>> main-page.onUnloaded");
}
export function onTap(args: observable.EventData) {
if ((<any>args.object).text.indexOf("(navigate)") !== -1) {
var entry: frame.NavigationEntry = {
moduleName: "./login-page",
context: "Context from navigate"
};
frame.topmost().navigate(entry);
}
else {
var fullscreen = (<any>args.object).text.indexOf("(full-screen)") !== -1;
page.showModal("./modal-views-demo/login-page", "some custom context", function (username: string, password: string) {
showModal(fullscreen);
}
}
function showModal(fullscreen?: boolean) {
page.showModal("./modal-views-demo/login-page", "Context from showModal", function (username: string, password: string) {
console.log(username + "/" + password);
label.text = username + "/" + password;
}, fullscreen);

View File

@ -1,7 +1,14 @@
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="onLoaded" navigatedTo="onNavigatedTo" id="_mainPage" backgroundColor="Red">
<Page xmlns="http://www.nativescript.org/tns.xsd" id="_mainPage" backgroundColor="Red"
navigatingTo="onNavigatingTo"
loaded="onLoaded"
navigatedTo="onNavigatedTo"
navigatingFrom="onNavigatingFrom"
navigatedFrom="onNavigatedFrom"
unloaded="onUnloaded">
<StackLayout backgroundColor="PaleGreen">
<Button text="Login (small)" tap="onTap" />
<Button text="Login (full-screen)" tap="onTap" />
<Button text="Login (navigate)" tap="onTap" />
<Label id="label" text="Anonymous"/>
<Button text="Close Modal" tap="onCloseModal" />
</StackLayout>

View File

@ -6,7 +6,9 @@ import page = require("ui/page");
export function onShownModally(args: ShownModallyData) {
TKUnit.wait(0.100);
var modalPage = <page.Page>args.object;
if (args.context) {
args.context.shownModally = true;
}
TKUnit.assert(frame.topmost().currentPage.modal = modalPage, "frame.topmost().currentPage.modal should be equal to the page instance on page.shownModally event handler.");
args.closeCallback("return value");
}

View File

@ -156,23 +156,31 @@ export function test_PageNavigation_EventSequence() {
addLabelToPage(testPage);
testPage.on(PageModule.Page.navigatingToEvent, function (data: PageModule.NavigatedData) {
eventSequence.push("onNavigatingTo");
TKUnit.assertEqual(data.context, context, "onNavigatingTo: navigationContext");
eventSequence.push("navigatingTo");
TKUnit.assertEqual(data.context, context, "navigatingTo: navigationContext");
});
testPage.on(PageModule.Page.loadedEvent, function (data) {
eventSequence.push("loaded");
});
testPage.on(PageModule.Page.navigatedToEvent, function (data: PageModule.NavigatedData) {
eventSequence.push("onNavigatedTo");
TKUnit.assertEqual(data.context, context, "onNavigatedTo : navigationContext");
eventSequence.push("navigatedTo");
TKUnit.assertEqual(data.context, context, "navigatedTo : navigationContext");
});
testPage.on(PageModule.Page.navigatingFromEvent, function (data: PageModule.NavigatedData) {
eventSequence.push("onNavigatingFrom");
TKUnit.assertEqual(data.context, context, "onNavigatingFrom: navigationContext");
eventSequence.push("navigatingFrom");
TKUnit.assertEqual(data.context, context, "navigatingFrom: navigationContext");
});
testPage.on(PageModule.Page.navigatedFromEvent, function (data: PageModule.NavigatedData) {
eventSequence.push("onNavigatedFrom");
TKUnit.assertEqual(data.context, context, "onNavigatedFrom: navigationContext");
eventSequence.push("navigatedFrom");
TKUnit.assertEqual(data.context, context, "navigatedFrom: navigationContext");
});
testPage.on(PageModule.Page.unloadedEvent, function (data) {
eventSequence.push("unloaded");
});
return testPage;
@ -181,8 +189,8 @@ export function test_PageNavigation_EventSequence() {
helper.navigate(pageFactory, context);
helper.goBack();
var expectedEventSequence = ["onNavigatingTo", "onNavigatedTo", "onNavigatingFrom", "onNavigatedFrom"];
TKUnit.arrayAssert(eventSequence, expectedEventSequence, "Actual event sequence is not equal to expected.");
var expectedEventSequence = ["navigatingTo", "loaded", "navigatedTo", "navigatingFrom", "navigatedFrom", "unloaded"];
TKUnit.arrayAssert(eventSequence, expectedEventSequence, "Actual event sequence is not equal to expected. Actual: " + eventSequence + "; Expected: " + expectedEventSequence);
}
export function test_NavigateTo_WithContext() {

View File

@ -66,3 +66,44 @@ export function test_WhenPageIsLoadedItCanShowAnotherPageAsModal() {
helper.goBack();
}
}
export function test_WhenShowingModalPageUnloadedIsNotFiredForTheMasterPage() {
var masterPage;
var masterPageUnloaded = false;
var modalClosed = false;
var modalCloseCallback = function (returnValue: any) {
TKUnit.wait(0.100);
modalClosed = true;
}
var loadedEventHandler = function (args) {
var basePath = "ui/page/";
args.object.showModal(basePath + "modal-page", null, modalCloseCallback, false);
};
var unloadedEventHandler = function (args) {
masterPageUnloaded = true;
};
var masterPageFactory = function (): PageModule.Page {
masterPage = new PageModule.Page();
masterPage.id = "master-page";
masterPage.on(view.View.loadedEvent, loadedEventHandler);
masterPage.on(view.View.unloadedEvent, unloadedEventHandler);
var label = new LabelModule.Label();
label.text = "Modal Page";
masterPage.content = label;
return masterPage;
};
try {
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => { return modalClosed; });
TKUnit.assert(!masterPageUnloaded, "Master page should not raise 'unloaded' when showing modal!");
masterPage.off(view.View.loadedEvent, loadedEventHandler);
masterPage.off(view.View.unloadedEvent, loadedEventHandler);
}
finally {
helper.goBack();
}
}

View File

@ -1112,4 +1112,12 @@ export class View extends proxy.ProxyObject implements definition.View {
animation.target = that;
return new animationModule.Animation([animation]);
}
public toString(): string {
if (this.id) {
return this.typeName + `<${this.id}>`;
}
return this.typeName;
}
}

View File

@ -172,8 +172,13 @@ function onFragmentShown(fragment: PageFragmentBody) {
frame._currentEntry = entry;
// notify the page
frame._addView(page);
// onFragmentShown is called before NativeActivity.start where we call frame.onLoaded
// We need to call frame.onLoaded() here so that the call to frame._addView(page) will emit the page.loaded event
// before the page.navigatedTo event making the two platforms identical.
if (!frame.isLoaded) {
frame.onLoaded();
}
page.onNavigatedTo(isBack);
frame._processNavigationQueue(page);
}
@ -501,7 +506,9 @@ var NativeActivity = {
onStart: function () {
this.super.onStart();
trace.write("NativeScriptActivity.onStart();", trace.categories.NativeLifecycle);
if (!this.frame.isLoaded) {
this.frame.onLoaded();
}
},
onStop: function () {

View File

@ -300,9 +300,17 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
return;
}
let newEntry: definition.BackstackEntry = viewController[ENTRY];
let newPage = newEntry.resolvedPage;
// For some reason iOS calls navigationControllerDidShowViewControllerAnimated twice for the
// main-page resulting in double 'loaded' and 'navigatedTo' events being fired.
if (!(<any>newPage)._delayLoadedEvent) {
return;
}
let backStack = frame.backStack;
let currentEntry = backStack.length > 0 ? backStack[backStack.length - 1] : null;
let newEntry: definition.BackstackEntry = viewController[ENTRY];
// This code check if navigation happened through UI (e.g. back button or swipe gesture).
// When calling goBack on frame isBack will be false.
@ -337,8 +345,6 @@ class UINavigationControllerImpl extends UINavigationController implements UINav
frame._navigateToEntry = null;
frame._currentEntry = newEntry;
let newPage = newEntry.resolvedPage;
// In iOS we intentionally delay the raising of the 'loaded' event so both platforms behave identically.
// The loaded event must be raised AFTER the page is part of the windows hierarchy and
// frame.topmost().currentPage is set to the page instance.

View File

@ -80,7 +80,7 @@ class UIViewControllerImpl extends UIViewController {
}
}
trace.write(this._owner + ", native frame = " + NSStringFromCGRect(this.view.frame), trace.categories.Layout);
trace.write(owner + ", native frame = " + NSStringFromCGRect(this.view.frame), trace.categories.Layout);
}
else {
owner._updateLayout();
@ -115,6 +115,10 @@ class UIViewControllerImpl extends UIViewController {
}
trace.write(owner + " viewDidDisappear", trace.categories.Navigation);
if (owner.modal) {
// Don't emit unloaded if this page is disappearing because of a modal view being shown.
return;
}
owner._enableLoadedEvents = true;
owner.onUnloaded();
owner._enableLoadedEvents = false;