Knockout - Unable to parse bindings only within Android WebView - android

I have the following setup on my site. It is working fantastically on any standard browser and any normal mobile browser:
Model:
function SearchTerm(data) {
this.Term = ko.observable(data.Term);
this.Stamp = ko.observable(data.Stamp);
}
ViewModel:
function HistoryViewModel() {
//Data
var self = this;
self.searchTerms= ko.observableArray([]);
// Load history from server
$.ajax({
type: "GET",
url: "/api/history/",
data: null,
success: function (msg) {
var terms = $.map(msg, function (item) { return new SearchTerm(item) });
self.searchTerms(terms);
},
error: function (xhr, ajaxOptions, thrownError) {
// Error Handling
}
});
}
View:
<div class="title">
Search History
<div class="closeBtn"></div>
</div>
<div id="historySection">
<ul class="searches" data-bind="foreach: searchTerms">
<li class="historyItem">
<div class="timestamp"><span data-bind="'text': Stamp"></span></div>
<span data-bind="'text': Term"></span>
</li>
</ul>
<a class="loadMore">Load 10 More Searches ยป</a>
</div>
All of this works perfectly on everything I test, with one major exception: The WebView control for Android. Whenever I run this page there, I get the following exception:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: Stamp is not defined;
Bindings value: 'text': Stamp at {url}/Scripts/libs/knockout-2.2.1.js:5
I'm at my wit's end. I've tried every possible combination of data-bind syntax (as you can see I've tried the single quotes too, as I'd seen a post related to that at some point). My only guess now is that this might be related to my order of scripts. I do currently reference knockout before I reference jquery - could this be the issue? I hadn't seen this as an issue anywhere else, but at this point, I'm willing to try anything.
Update:
All of my other knockout binding viewModels and bindings are working fine on Android, but none of them occur on page load. So that led me to look at changing the ajax to a method and trying to delay it's calling. So I implemented the following addition to my viewmodel:
//Operations
self.loadRecent = function () {
// Load search history
$.ajax({
type: "GET",
url: "/api/history/",
data: null,
success: function (msg) {
var terms = $.map(msg, function (item) { return new History(item) });
self.history(terms);
},
error: function (xhr, ajaxOptions, thrownError) {
//Error handling
}
});
In my page, here is what the initial binding looks like - it occurs at the bottom of the page:
var history = new HistoryViewModel();
ko.applyBindings(history, $("#historySection")[0]);
//Call page event handlers
$(window).load(function () {
// Load in our search history
history.loadRecent();
});
Unfortunately, same error as before. However, this is where things get really interesting. Even when I comment out the call to the loadRecent operation, I still get the error! To me, this means, for some reason, for this model only, the foreach is still trying to iterate, even though nothing exists. Do my assumptions sound correct? Anyone see anything I'm blatently missing here that would cause this issue? None of my other viewModels and views are having this issue. I have 2 others I'm binding to and they both have foreach's in them and do not seem to experience this issue.
UPDATED 2013.05.20
I updated some of the code throughout to reflect some function/object renaming I did, as I started to worry that something was getting out of sorts with me having a variable named history with a function named history (silly, I know, but I'm desparate).
I also wanted to post an additional finding. I decided to go ahead and try removing the code for loading the history completely from any loading or ready event. Rather, I was able to separate out this data load into a button click. Here is what the button click handler looks like:
$(".menuButton").on("click", "", function () {
history.searchTerms([]);
history.loadRecent();
});
Fun fact when I do this though, in Android-only. I still get an error on load, in the bindings:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: searchTerms is not defined;
Bindings value: foreach: searchTerms at {url}/Scripts/libs/knockout-2.2.1.js:5
Another interesting tidbit - if I were to remove the html from the view altogether, meaning there are no bindings, yet still leave the code for the SearchTerm model and the HistoryViewModel, I still get an error from the WebView in Android, when the button is clicked:
Uncaught TypeError: Object #<History> has no method 'searchTerms' at {url}:681
Sorry for so much posting, but I'm desperate to figure out what's wrong with this.

maybe webview does some wacky stuff with HTML 5 history api and its resetting it on top of your history view model. have you tried changing the vm name to historyVM or something? i would argue you shouldnt be naming your viewmodels to match things being used by the browser itself (like, dont name your vm 'window' or 'document')

Related

Ionic Angular NFC Changedetection Problem

I have a problem using an actual Ionic Angular in combination with NFC reader.
I can successfully read the NFC Tag with the example provided in:
https://ionicframework.com/docs/native/nfc
The Problem that i have is, that after the reading of the NFC somehow the Angular ChangeDetection is broken and the changes are not displayed. When i click on a button that does nothing, the changes are displayed.
I know i can trigger the ChangeDetection manually, which would work, but then i would have to trigger the ChangeDetection everywhere in the component, which i think should not be the case.
Does anyone have an idea what i am doing wrong?
First i thought maybe it has something to do with the observable, but i tried adding a interval observable, which works just like expected.
Template:
<p><ion-button (click)="startNFC()">start NFC readermode</ion-button></p>
<p><ion-button (click)="startNFCListener()">start NFC listener</ion-button></p>
<p><ion-button (click)="startInterval()">start Interval</ion-button></p>
<p><ion-button (click)="doNothing()">do nothing</ion-button></p>
<p>
{{nfcReading}}
{{nfcTag}}
<ion-spinner *ngIf="nfcReading"> </ion-spinner>
</p>
Code:
startNFC() {
console.log("startNFC");
this.nfcReading = true;
let flags = this.nfc.FLAG_READER_NFC_A | this.nfc.FLAG_READER_NFC_V;
this.readerModeSub = this.nfc.readerMode(flags).subscribe(
tag => {
console.log(JSON.stringify(tag));
this.nfcTag = this.nfc.bytesToHexString(tag.id);
this.nfcReading = false;
this.readerModeSub.unsubscribe();
},
err => {
console.log("Error reading tag", err);
this.nfcReading = false;
}
);
}
doNothing() {
// this really does nothing... it is just to demonstrate that this triggers the changedetection
}
i get can see that the subsciption event is triggered in console, but it is not shown in the HTML.
When clicking the do nothing Button. The tag is shown.
I created a fresh type=angular blank project and test on a Google Pixel 3a hardware.
i have uploaded a sample project to demonstrate my problem.
https://github.com/autlunatic/Ionic-NFC-Android-Test/

Protractor locators does not work in PageObjectModel structure

Framework: Appium + Protractor + Cucumber + Typescript using POM model
I have the protractor framework for appium designed in POM structure
The initial page of the app will identify the locators calling in a different ts file and the functions such as tap, isDisplayed calling it in a different ts file.
But once it passes the initial pages in the app,say 3 pages. the locators are not identified which are calling other function, but they are identified when they are passed directly ( like driver.findelements(by.css('')).click ) this works.
The problem is I can't pass this code like this within the step definition .ts file always as it is not a good structure
Note: By the way, this script was working fine earlier.
tried to test using a different workaround, like building the binary again, trying to run on android and ios application, downgrading or upgrading the node packages. But nothing solved the problem. has anyone faced this kind of issue. Any suggestions or solutions for this problem, please?
Code which works: (Passing the locators directly in the function, rather than from the onboarding.ts file will work)
Then(/^VIC should be selected from the state or territory drop down$/, async () => {
await browser.driver.findElement(by.css('button[sp-automation-id=\'select-state-toggle\']')).click();
await browser.driver.findElement(by.css('page-action-sheet label[sp-automation-id=\'action-sheet-option-VIC\']')).click(); });
Code which does not work: (Onboarding.ts file contains the locators defined for State and VIC same as the above code block. But reading from there it does not work.)
Then(/^VIC should be selected from the state or territory drop down$/, async () => {
await AutomationAction.tap(Onboarding.State);
await AutomationAction.tap(Onboarding.VIC); });
Code which works (The below code is called before the above code block, it's a page before calling the above pages)
Then(/^I enter the mobile number and tap next button on the your mobile number screen$/, async () => {
MobileNo = AutomationAction.getMobileNumber("mobileNumber");
SameMobileNo = MobileNo;
await AutomationAction.sendKeyText(Onboarding.InputMobileNo,MobileNo);
await AutomationAction.tap(Onboarding.Next_BTN_YourMobileNumber);
});
Because of the page where it is failing the automation thinks its as non-angular page and the locators used to fail or not locate them when calling it in a different function. When I introduced browser.ignoreSycnhronization=true to make Angular sync for non-angular apps/pages it worked.

How to create async call with jquery with android using phonegap?

I have seen background service in phonegap and integrated successfully but, I need to make an ajax call to get the data from server.This call need to be happen in background process without disturbing the UI. I am unable to do this using this plugin.
And my code look like,
$.ajax({
type: "POST",
url: "http://example.com/xxx",
dataType: "xml",
async:true,
success: function (data) {
var serializer = new XMLSerializer();
var xmlString = serializer.serializeToString(data);
var finalxml= xmlString.replace(/</g, '<').replace(/>/g, '>').replace('<br/>','\n');
window.localStorage.setItem("xml_Questions",finalxml);
}
});
This ajax call has to be done with async(background) call while user doing something..
But user is not able to do anything until get the response.So I have followed Background service plugin for phonegap, but I am not able to implement this method with that plugin.
Can we do this?
Can anybody help me?
Thank you in adv...
Your $.ajax code looks right to me, so it shouldn't block the app behavior.
I don't completely understand your question... user shoudn't be allowed to do anything until the localstorage.setitem is set? If this is right, you could use jquery to enable some kind of "NEXT" button after the setItem instruction, so the user won't be able to move on until the async call is done.

JSfiddle : how to create multi-JS/CSS/HTML files project?

Is there a way to create a serious HTML/CSS/JS project with multiple HTML, CSS, JS files on JSfiddle.net ?
If yes, how to do it ?
I want to create a basic mobile apps based on HTML/CSS/JS, about a dozen of HTML/CSS/JS files. I would like to develop it all on JSfiddle, my favorite Online JavaScript IDE. But JSfiddle.net while a clean way to test projects stays limited to:
1 html file (personal)
1 CSS file (personal)
1 JS file (personal)
several external resources (CSS, JS libs, data) which request you another webhosting.
The official doc suggesting Github hosting for 1HTML/1JS/1CSS/someDataFiles is not satisfying. I wish all on JSFiddle, and more files in my project.
You can do it inside a jsFiddle but there are few limitations, and you are probably not going to be satisfied with it.
You can test only 1 HTML multiple pages template. But in case of jQuery Mobile framework this will be enough, as you can place numerous jQM pages inside a 1 html file.
For example, this is my jsFiddle template when helping to this group: http://jsfiddle.net/Gajotres/yWTG2/
You cant use normal form submitting. Instead you should use ajax to sumbit form data.
In my other answer you can find solutions for ajax form submitting and how to send parameters during the page transition: jQuery Mobile: Sending data from one page to the another
In case you want to communicate with an remote host:
var ajax = {
sendRequest:function(save_data){
$.ajax({url: 'http://localhost/JSONP_Tutorial/json.php',
data: save_data,
async: true,
beforeSend: function() {
// This callback function will trigger before data is sent
$.mobile.showPageLoadingMsg(true); // This will show ajax spinner
},
complete: function() {
// This callback function will trigger on data sent/received complete
$.mobile.hidePageLoadingMsg(); // This will hide ajax spinner
},
success: function (result) {
if(result == "true") {
$.mobile.changePage( "#index", { transition: "slide"} ); // In case result is true change page to Index
} else {
alert('Login unsuccessful, please try again!'); // In case result is false throw an error
}
// This callback function will trigger on successful action
},
error: function (request,error) {
// This callback function will trigger on unsuccessful action
alert('Network error has occurred please try again!');
}
});
}
}
jsFiddle has a stupid policy where they want to prevent usage of full HTML files. They are trying to enforce this with stupid error warnings in HTML content part. You will need to have something like firebug plugin for Firefox or Chrome to remove this stupidity. Or you can even do it with Grease Monkey plugin.
In case you want to use full HTML template like in this example: http://jsfiddle.net/Gajotres/yWTG2/ you will need to use your javascript code in onDomready state.
Some functionalities are not going to work. Like window.orientationchange event.

Best practice for constructing a jQuery mobile page by json parse

I am developing an android app using phonegap which call an api on page load getting a json object as a return parameter.
now I have to construct the page using jQuery mobile by extracting values from the recieved object.
So I am asking what will be the best practice for this which can reduce its loading time.
Thanks for helping.
Presently what I am doing
<script>
$(document).ready( function() {
$.ajax({
url : "demourl.com",
type : "GET",
success : function(data) {
var obj = $.parseJSON(data);
$("#results").html(obj.messagedetails[0].spamReason.userApprove);},
fail : function() {
$("#notification").text("Try again after some time.");
}
});
});
</script>
Getting objects from this call and setting it in
<div id="results"></div>
There's nothing you can do about $.ajax / $.getJSON loading time, it will depend on your internet connection.
Never load data during the page transitions, do it ether before page change or after a page change. Best practice is to do it before page change (just show AJAX loader to indicate AJAX content is loading).
When data is loaded use .append( and not .html( to append data.
In case you are using each or for loop to add a dynamic content (laoded with an AJAX) UNDER NO CIRCUMSTANCES enhance page markup (apply jOuery Mobile style to new added content) during each loop, THIS IS A HUGE TIME SINK, do it only after all content has been added.
You can find more about this in my ARTICLE, or find it HERE.
Do not use document ready, it could trigger before page is loaded. Instead use correct page event.
Find more about document ready vs page events in my other ARTICLE, here you will also find a way to benchmark jQuery Mobile and some statistic about how much time is needed for some jQM actions.

Categories

Resources