Populate HTML forms in WebView - nativescript

I have a NativeScript-5 app (TypeScript flavor) with a simple page like this:
<Page class="page">
<StackLayout>
<WebView id="webView" loaded="onWebViewLoaded" src="http://google.com" />
</StackLayout>
</Page>
After loading the web page (onWebViewLoded()), I would like my app to populate certain HTML fields (access by id or name) and finally post the surrounding HTML form. Can this be done at all?
I know that there is a Nativescript-WebView-Interface plugin, but it requires me to embed a script in the loaded page (and I can't do this, because I don't own the page I am loading). So I assume I need another approach.
If anybody has a solution that works at least on Android, that would be great. Thanks guys!
Update:
In order to avoid misunderstandings: Submitting the page is optional. The important part is to load a web page and auto-fill some values that my app already knows (so the user does not have to enter these values in the HTML form himself).

You may easily execute JavaScript in the webpage context in Android.
export function onLoadFinished(args: EventData) {
const webView = (<WebView>args.object).nativeView;
if (isAndroid) {
// Make sure the element index is valid
const javaScript = `document.getElementsByTagName("input")[2].value = "It works!"`;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(javaScript, null);
} else {
webView.loadUrl(`javascript:${javaScript}`);
}
}
}
Here is the Playground Sample
It's also possible with iOS, but you may have to override / extend the existing {N} WebView inject JavaScript upon creating native view.

Related

Flaky cypress test with Svelte: Button is sometimes clicked, sometimes not

I am testing my SvelteKit site with Cypress. I sometimes experience flaky tests, similar to what has been described here: https://www.cypress.io/blog/2019/01/22/when-can-the-test-click/. In short, Cypress sometimes finds and clicks a button before the event listeners are attached - as a result, the click goes nowhere. The proposed solution is to simply re-try clicking until the appropriate listeners have been attached. That works in my case as well. However, though I do understand why this can be an issue in the example given in the blog post (it's a large calendar modal), I find it hard to justify that this issue arises when using a simple Svelte button.
Here is a simple example of a button that reveals some content when clicked:
<script>
let hide = true;
</script>
<button
on:click={() => {
console.log('clicked');
hide = false;
}}>
Show
</button>
<span class:hide>Content</span>
<style>
.hide {
visibility: hidden;
}
</style>
The corresponding test sometimes passes, sometimes fails:
it('reveals content on click', () => {
cy.contains('Show').click();
cy.contains('Content').should('be.visible');
});
Again, I am aware this can be fixed by re-trying to click the button. And if this is what it takes to make Cypress work with Svelte/SvelteKit, then that's fine with me. But I am wondering: Why would this even be an issue?
Minimal reproduction repo: https://github.com/sophiamersmann/test-svelte-kit-cypress
I think the problem lies with Vite, which uses ES modules to load the page and it's components.
Adding an intercept before the cy.visit() seems to give consistent results.
(Note the URL to intercept may vary, you can get it from the last entry in devtools Network).
beforeEach(() => {
cy.intercept('index.svelte?svelte&type=style&lang.css').as('svelte')
cy.visit('/');
cy.wait('#svelte')
});
Using cypress-grep to burn-test
npx cypress run --env burn=100
With intercept
Without intercept
Why is it not hydration?
If you create an equivalent Svelte app with hydratable set to true, it will pass the burn test - IMO because it uses rollup instead of vite to deliver the app to the browser.
SvelteKit will by default do server side rendering (SSR), which means the complete HTML is sent to the browser, including the button. That HTML then needs to be hydrated afterwards to become interactive. This means that some code runs so that Svelte connects to the HTML that already exists. Cypress is likely "too fast" here and clicks the button before that hydration step is completed, therefore nothing happens.
It does not happen with pure Svelte because there's no SSR involved. There's a blank index.html page initially which is completely filled by Svelte's JavaScript inside the browser, so the moment the button is visible, the event listener and everything else is already initialized by Svelte.
Comparison by steps:
SvelteKit with SSR:
Go to page X
Page X is rendered on the server
Page X is sent to the browser, with the complete HTML
Svelte hydrates the HTML (Race condition with Cypress' click test)
Completed
Pure Svelte or SvelteKit without SSR:
Go to page X
Blank page is sent to the browser
Svelte constructs and initializes the HTML inside the browser (No race condition with Cypress' click test)
Completed
Component
<script>
import { onMount } from 'svelte'; // <- Here
let init = false;
onMount(() => {
init = true;
});
let hide = true;
</script>
<button
data-init={init}
on:click={() => {
console.log('clicked');
hide = false;
}}>
Show
</button>
<span class:hide>Content</span>
<style>
.hide {
visibility: hidden;
}
</style>
Cypress
it('reveals content on click', () => {
cy.get('[data-init=true]').should('exist'); // <- Here
cy.contains('Show').click();
cy.contains('Content').should('be.visible');
});

Use querySelector in nativescript;

i want to play an animation when a new message is added in the DOM.
but i don't know how to find my object and edit it with code in (this.zone.run) function :
addMessage(message: string){
this.messages.unshift(message);
// renderer
this.zone.run(() => {
});
}
here's the app.component.html
<StackLayout #container>
<ScrollView>
<WrapLayout #red>
<Label class="message" *ngFor="let message of messages" [text]="message"></Label>
</WrapLayout>
</ScrollView>
</StackLayout>
i want to edit the first child of the WrapLayout element
There is no DOM with NativeScript.
However a major community contributor wrote a plugin to help transition web developers into native development with NativeScript. This plugin provides helper methods that you'll find familiar to the web and DOM. https://github.com/NathanaelA/nativescript-dom
Just remember these are helper methods are not something provided out of the box by NativeScript. You can get any view by its id in NativeScript several ways and during different events (page/frame and component level).
I recall there's no page component with NativeScript with angular but I think you still have the frame module which you could do something like
frame.topmost().currentPage.getViewById('yourID');
Making sure you import(require) the frame module.

What replaced ClientScriptManager in MVC?

I'm converting an ASP.NET Forms app (that I didn't write) to an MVC 3 app. The ClientScriptManager is used in the old app. ClientScriptManager doesn't exist in MVC 3. What replaced ClientScriptManager? I've done enough reading to know that CSM has something to do with AJAX functionality in ASP.NET; it registers "sets" of js scripts and functions somewhat akin to how EditorTemplates work for sets of similar controls. I'm not necessarily looking to implement a CSM in MVC; I just want to know what practices Microsoft put in place that rendered CSM obsolete.
ASP.Net MVC was designed to give you complete control over the HTML and js, rather than having the framework render these for you, as with ASP.Net. So, nothing really "replaces" the ClientScriptManager. As a result, it is entirely up to you how you handle your js.
You can do something simple like include <script ...> tags to reference inline script or js files in your _Layout.cshtml file, or you could use some sort of JavaScript Loader like RequireJS or come up with your own scheme entirely using "Html Helpers" or something.
MVC 4 introduced the concept of bundling which lets you define collections of scripts as "bundles" and have MVC automatically minify and merge them together for you when you reference them in your Views like this :
#Scripts.Render("~/bundles/jquery")
Below is an example for rendering the JavaScript(Function) from Controller.
Controller
public ActionResult Index(int? id)
{
ViewBag.MyAlert = "<script type='text/javascript'>MyAlert();</script>";
}
View
<script src="Your Path" type="text/javascript"></script>
#Html.Raw(ViewBag.MyAlert)
JS
function MyAlert() {
alert('Hi');
}
Below is an example for rendering the JavaScript(File) from Controller.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
StringBuilder sb = new StringBuilder();
sb.Append("<script src='Your Path' type='text/javascript'></script>");
filterContext.HttpContext.Response.Write(sb.ToString());
}
So using this approach you do not need to mentioned following code in View.
<script src="Your Path" type="text/javascript"></script>

sIFR not working with prototype AJAX page load

I have a website with two pages. A and B. When you click on a link in page A, it will uses the Prototype Ajax.Updater() to load the link page (page B) into a div on the page (Page A).
When page B loads into page A, the sIFR replacements are not working and the tag inner text is not even showing.
I have tried doing a sIFR.redraw() when the page has loaded into the div, with no success.
When i view page B in the browser by itself, it works perfectly.
Is it possible to insert HTML into a DIV tag on a page using AJAX and have the sIFR display properly?
I would imagine that you probably need to re-initialise sIFR within the onComplete callback of Ajax.Updater
This is the way I ended up doing this
new Ajax.Updater('content', url, {
onComplete:function(){
sIFR.replace(font, {
selector: '#content h2'
});
}
});
You could put this snippet in your html code:
<script type="text/javascript">
function pageLoad(sender, args) {
sIFR.replace(font, { selector: '#content h2' });
}
</script>
It will run the sIFR replacements each time the page reloads. This is on normal PostBack and Ajax postbacks. Make sure you have included a ScriptManager instance on your page.
mathijsuitmegen:
Your solution worked perfectly on an application I'm using where the ScriptManager was already in use.
Infact, The function was simply slotted into my sifr-config.js file and worked perfectly meaning my HTML wasn't cluttered.

AJAX modal dialog, fire onload if referer == <whatever>

I'm trying to change my index.html to show a modal window if the referer to my site == (eg, if they come from Google, show a "Welcome Googler" dialog box with an image inside of it).
I'm using FancyBox, but I'm not married to it.
Any suggestions on how to code it? I'm a C++ programmer -- Javascript isn't my forte, so straight examples would be very much appreciated.
Thanks in advance.
You're going to need a couple things: document.referrer, and jQuery UI. jQuery UI makes dialog boxes trivially easy.
You can find an in depth example from the documentation page but for the most part, this is what you are going to need:
<script type="javascript/text">
if (document.referrer.indexOf('google.com') > -1){
$("#my-dialog").dialog("open");
}
// this is the jquery code to set up the dialog box
$(function() {
// options would go inside the dialog() function
$("#dialog").dialog();
});
</script>
Needed HTML:
<div id="my-dialog">
This is where things get displayed
</div>

Resources