Setter on text only called after edit on Android, live on iOS - binding

When I'm binding text to an input on iOS, my setter is called each time a character is added, on iOS, but not on Android.
If I put a breakpoint on a property that is binded to a TextField in iOS, each time a character is entered, the property setter will be called, but not on an Android EditText.
It makes more complex ViewModels with several input attached to getter/setter tested on iOS completely useless on Android since it cannot be used.
Is there a way to make the "MvxBind="Text SomeProperty" acting like iOS on Android?
Events like "AfterTextChanged" (any binding to a command) aren't property-friendly, and would break my ViewModel. I don't want to have a platform-dependent workaround.
[Edit]
// Droid. It calls the TotalAmount setter once the editing is done.
<EditText local:MvxBind="Text TotalAmount,
Mode=OneWayToSource; Text TotalAmountString, Mode=OneWay" />
// Touch. It calls the TotalAmount setter on key press.
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmount).OneWayToSource();
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmountString).OneWay();
By the way, the displayed property is always formatted with a dollar sign, that's why I'm using an half-duplex approach for binding.
Appart from this live (iOS) versus after-edit (Droid) problem, the bindings are working well.

The default behaviour for TwoWay Text binding on both Android and iOS is to do per character binding.
You can see this behaviour in, for example, the N=0 video at 18:43 - http://youtu.be/_DHDMNB_IeY?t=18m43s
If you are not seeing this behaviour in your EditText then I guess it might be down in some way to your app or perhaps to a bug (e.g. perhaps in OneWayToSource binding somehow - this certainly isn't as commonly used as other binding modes).
To workaround this, I can only think to suggest:
Log it as an issue with a reproducible case (github repo) on GitHub/MvvmCross - someone there might be able to help - or you might be able to fix it yourself.
Try TwoWay binding instead
Try creating your own custom binding or your own custom control - this is actually very easy to do - see the tutorials N=28 and n=18 on http://mvvmcross.blogspot.com - for example you could try inheriting from EditText to create something like;
public class MyEditText : EditText {
public MyEditText(Context c, IAttributeSet a) {
this.AfterTextChanged += (s,e) => MyTextChanged.Raise(this);
}
public event EventHandler MyTextChanged;
public string MyText {
get { return Text; }
set { Text = value; }
}
}

Related

Issue adding an Emoji to TextInput for React-Native on iOS

I've been having an issue with maxLength on <TextInput/>in ReactNative and I'm pretty sure its only happening on iOS.
The problem:
If the last character of an input is an emoji, and that emoji puts you over the maxlength number then it deletes the entire input! An example of it is if the maxLength is set to 5 and the input is "xxxx" (4 characters) and then you add an emoji the entire input text gets deleted. I'm sure this has something to do with emojis mostly being 2 characters but I can't seem to find an "eloquent" work around!
Snack to see exactly what I'm talking about:
(I've only been able to replicate it in iOS)
https://snack.expo.io/#sararan/textinput
Things I've tried:
Adding an onKeyPress event (that gets hit before the onChangeText event) that calls e.stopPropagation() and e.preventDefault() (both for good measure ;) ). But it doesn't actually stop the event and I'm thinking it has to do with how react handles events and maybe that it's already bubbled up by this time?
Taking out maxLength altogether and creating my own rules that splices the input if its over the length I want and then replaces any special characters something like...
const onChangeText = (value) => {
if (value.length > 5) {
value = value.slice(0, 30).replace(/[^\w\s]/gi, '');
}
setText({ value });
};
but this solution seems to cause the flickering that we all hate with these types of solutions.
I'm wondering if anyone might have run into this before and might have a more eloquent solution? Thank you for your help!

ZK Binding NotifyChange not work on onChanging Event?

I currently use ZK for web-development. Here is my case:
I implement instant search, once text change=> perform search.
Zul File
<textbox id="textSearch" sclass="search_text">
<attribute name="onChanging">
lbOnChangingSearch.setValue(event.getValue());
vm.onChangingTextSearch();
</attribute>
</textbox>
<label id="lbOnChangingSearch" visible="false"></label>
<grid id="gridChapter" model="#load(vm.chapterInPage)">
....
</grid>
Controller code
ListModelList<ChapterJoinComic> chapterInPage;
public ListModelList<ChapterJoinComic> getChapterInPage() {
return chapterInPage;
}
#NotifyChange({ "topComics", "chapterInPage"} )
#Command
public void onChangingTextSearch() {
FilterObject fo = getFilterObject();
fo.setSearch_str(lbOnChangingSearch.getValue());
//
doSearch(fo); // Change chapterInPage
// Manually post Not
BindUtils.postNotifyChange(null,null,this.chapterInPage,"chapterInPage");
}
Problem
After call onChangingText search, Grid dont update databinding.
But if I continue change text (more call onChangingTextSearch ). The Grid will update, but the updated value is the previous value.
It seems the Grid is a-step slower than my latest Model object.
Note If I use onOK instead of onChanging event, the databinding works well.
Anyone can help me. Thanks in advance!
In addition of Malte his answer.
Textbox only sends data to the server with the onChange event to avoid needless network traffic.
If you want to send data to the server with the onChanging event, you need to do :
<textbox instant="true" />
In this case the client will update almost instantly to the server (if you type fast, it will be when you stop typing)
You should remove the BindUtils.postnotifyChange when you use #NotifyChange already, and you use it wrong anyway: the third parameter should be this instead of this.chapterInPage. The JavaDoc explains that you need to specify the bean whose property is changing and the name of the property.
Furthermore, replace your onChanging attribute with the proper way to call a command:
<textbox id="textSearch" sclass="search_text"
onChanging="#command('onChangingTextSearch')" />
Consult the documentation for more information on how to use commands. I think because you do not use the command as a command, the #NotifyChange is not triggered. And your postNotifyChange is wrong, as I said.
Let me know if that works or if there are other problems remaining.
EDIT
I just re-created an example on my own, and it seems to work. Try it like this:
ViewModel --------------------------------
private String searchText = "";
#NotifyChange({"chapterInPage", "searchText"})
#Command
public void onChangingTextSearch(#BindingParam("text") String searchText)
{
this.searchText = searchText;
}
public String getSearchText()
{
return searchText;
}
public ListModelList<String> getChapterInPage()
{
return new ListModelList<>(searchText.split(""));
}
zul --------------------------------------
<textbox onChanging="#command('onChangingTextSearch', text=event.value)" />
<label id="lbl" value="#load(model.searchText)" />
<listbox model="#load(model.chapterInPage)" />
Note that I use command binding to call the search method in the model instead of calling it "manually" in an onChanging listener. This way, I actually execute it as a command, which triggers the notifyChange. When you call it like vm.onChangingTextSearch() in a listener, the #NotifyChange is ignored.
This way, it works as expected, and with every character typed (well, after a couple of millisenconds delay), the list updates. Another advantage is that you do not have to bind your label into the view model, something that zk's documentation discourages.
Can you try to link your zul and model like this and tell me if it works. If it doesn't, you might want to try to create an example on zkFiddle that re-produces your code's behavior.

Intercept dialog from <webview> and read the contents

I use this code to intercept a dialog from a webview but I can not see the content or interact with it:
Element webview= querySelector("#webview");
Map<String,String> map=new Map();
map["src"]=urlWebView+user;
webview.attributes.addAll(map);
querySelector("#webview_cont").style.visibility="visible";
window.addEventListener("dialog",(Event e){ //Use window or webview returns the same result
e.preventDefault();
... //What should I do here ??
} );
Any solution?
Thanks
Edit
Debug:
Open issue: https://code.google.com/p/dart/issues/detail?id=23556
The problem definitely lies with your usage of Dart's Event class.
It simply does not support the extra properties that Chrome is adding to the event: e.dialog, e.messageText, e.messageType.
It does not seem like there is a ready solution for that, at least not in chrome.dart.
Sadly, I don't know Dart well enough to give you a solution. You need to somehow extend that event class, possibly dropping to JS level.
This library, even if abandoned, should give you ideas on how to do that (by catching the JS-level event and stuffing the extra properties in CustomEvent's detail property), though implementing DialogController (which is not JSON-serializable) would be a bit trickier, I guess.

Pure Dart DOM possibility?

I am learning Dart and suddenly had an epiphany (or possibly, an epiphany):
Can I write a Dart web app where the "view" is done 100% in Dart?
I'm talking: absolutely no (none/zero/nadda) HTML files (.html). 100% Dart code. Something like:
class SigninView {
LabelElement signinLabel;
InputElement emailTextField;
InputElement passwordTextField;
ButtonElement signinButton;
// constructors, getters, setters, etc.
// Perhaps called from inside constructor...
void initUI() {
signinLabel = new LabelElement();
signinLabel.innerHTML = "<span class=\"blah\">Please sign in</span>";
emailTextField = new InputElement();
emailTextField.innerHTML = "<input type=\"text\" name=\"fizz\" placeholder=\"Email\"/>";
// ...etc.
// htmlFactory would be something I'd need to write myself (?)
String html = htmlFactory.newHTML(signinLabel, emailTextField, ...);
querySelector("#someDivTag").innerHTML = html;
}
}
In theory (that is, my intentions with the above code), as soon as the SigninView is created, it initializes a bunch of DOM elements and populates someDivTag with them.
Is this possible? If so am I "doing it right", or is there a different/preferred/standardized approach to this?
Does this introduce any additional/potential caveats (memory leaks), performance or security issues that I should be aware of?
If I were to adopt this strategy throughout my whole app, can I assume the app would be quicker to download (less HTML text), but slower to execute (dynamic DOM element creation)? If so, is there a way to somehow instantiate all the DOM elements my app will need up front (slowing down initial download time), and then only make certain elements visible as I wish to render different views/screens (thus speeding up execution time)?
You need an HTML file with the script tags for the Dart startup.
Anything else can be done in Dart.

MVVMCross for android - how to do binding in code?

I want to use MVVMCross, however for my android application I also want to use other libraries (sliding menu and action bar) which require me to inherit my activity classes from their custom class. This prevents me from inheriting MvxActivity, but I noticed that in MVVMCross for iOS, you can do all your bindings in code (see https://github.com/slodge/NPlus1DaysOfMvvmCross/blob/master/N-00-FirstDemo/FirstDemo.Touch/Views/FirstView.cs)
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(textEditFirst).To(vm => vm.FirstName);
set.Bind(textEditSecond).To(vm => vm.LastName);
set.Bind(labelFull).To(vm => vm.FullName);
set.Apply();
Is there any way to do that in Android?
Yes - you can use fluent bindings in Android if you want to.
Exactly the same code should work.
You'll need to get references to the ui controls using FindViewById<Type>(), then you can bind them.
For example, in TipCalc you can add identified controls like:
<EditText
android:id="#+id/FluentEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:textSize="24dp"
android:gravity="right"
/>
and then implement binding using:
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.View_Tip);
var edit = this.FindViewById<EditText>(Resource.Id.FluentEdit);
var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(edit).To(vm => vm.SubTotal);
set.Apply();
// for non-default properties use 'For':
// set.Bind(edit).For(ed => ed.Text).To(vm => vm.SubTotal);
// you can also use:
// .WithConversion("converter", "optional parameter")
// .OneTime(), .OneWay() or .TwoWay()
}
Additionally, you can convert any FooActivity into a data-binding MvxFooActivity by:
inheriting from FooActivity to provide events from lifetime events in an EventSourceFooActivity
inheriting from EventSourceFooActivity to provide a datacontext in an MvxFooActivity
you can then write your code inside activities inheriting from MvxFooActivity
To see, the code required, see:
https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid.Fragging/MvxEventSourceFragmentActivity.cs
https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid.Fragging/MvxFragmentActivity.cs
You'll see the same code in all the mvx adapted Activities - MvxActivity, MvxTabActivity, ... There is a little cut-and-paste here, but as much code as possible is place in shared extension methods.
In previous versions, people have used this technique to bind monogame and google ads activities - eg see Insert a Monogame view inside MvvmCross monodroid Activity

Resources