mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 04:41:36 +08:00
Merge pull request #1032 from NativeScript/loaded-twice
Resolved issues #789, #1021, #1031.
This commit is contained in:
@ -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();
|
||||
|
@ -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");
|
||||
closeCallback(usernameTextField.text, passwordTextField.text);
|
||||
console.log(">>> login-page.onLoginButtonTap");
|
||||
|
||||
if (closeCallback) {
|
||||
closeCallback(usernameTextField.text, passwordTextField.text);
|
||||
}
|
||||
else {
|
||||
frame.topmost().goBack();
|
||||
}
|
||||
}
|
@ -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"/>
|
||||
|
@ -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) {
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
|
@ -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>
|
||||
|
@ -6,7 +6,9 @@ import page = require("ui/page");
|
||||
export function onShownModally(args: ShownModallyData) {
|
||||
TKUnit.wait(0.100);
|
||||
var modalPage = <page.Page>args.object;
|
||||
args.context.shownModally = true;
|
||||
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");
|
||||
}
|
@ -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() {
|
||||
|
@ -65,4 +65,45 @@ export function test_WhenPageIsLoadedItCanShowAnotherPageAsModal() {
|
||||
finally {
|
||||
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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
this.frame.onLoaded();
|
||||
if (!this.frame.isLoaded) {
|
||||
this.frame.onLoaded();
|
||||
}
|
||||
},
|
||||
|
||||
onStop: function () {
|
||||
|
@ -300,10 +300,18 @@ 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.
|
||||
let isBack: boolean = currentEntry && newEntry === currentEntry;
|
||||
@ -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.
|
||||
|
@ -25,7 +25,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
let owner = this._owner.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
trace.write(owner + " viewDidLayoutSubviews, isLoaded = " + owner.isLoaded, trace.categories.ViewHierarchy);
|
||||
if (!owner.isLoaded) {
|
||||
@ -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;
|
||||
|
Reference in New Issue
Block a user