Customizing the IQKeyboardManager with Nativescript + javascript - nativescript

I've got a nativescript app and I'm using IQKeyboardManager with default settings.
I see there in an option to dismiss the keyboard on tap outside a text field with a typescript example for toggling it on and off:
private iqKeyboard: IQKeyboardManager;
public keepKeyboardOpenOnTouchOutside: boolean = true;
toggleKeepKeyboardOpen(): void {
this.iqKeyboard.shouldResignOnTouchOutside = !this.iqKeyboard.shouldResignOnTouchOutside;
}
But I don't understand how to go about it in normal javascript.
I want to set the keepKeyboardOpenOnTouchOutside variable with something like:
exports.loaded = function(args){
keepKeyboardOpenOnTouchOutside = false;
}
but I don't understand how I'm supposed to access the instance variable properly.

I missed a step in the documentation:
/// <reference path="./node_modules/tns-platform-declarations/ios/ios.d.ts" />
/// <reference path="./node_modules/nativescript-iqkeyboardmanager/index.d.ts" />
is required in references.d.ts and then I can initialize the variables like so:
const iqKeyboard = IQKeyboardManager.sharedManager();
iqKeyboard.keepKeyboardOpenOnTouchOutside = false;
iqKeyboard.shouldResignOnTouchOutside = true;

Related

Microsoft Teams App - showLoadingIndicator : true resulting in breaking config tab

I am trying to use showLoadingIndicator : true in my manifest file and as soon as I load the MicrosoftTeams.js file in my _host.cshtml file I call the javascript to notify success. But this breaks the config Tab. When I search for the item and select the result and click on save it throws error saying tabsettings can not be saved. But if I click on save second time , it works.:
_host.cshtml
<script src="https://res.cdn.office.net/teams-js/2.5.0/js/MicrosoftTeams.min.js"
integrity="sha384-0lOzlvRkoNWAcLkbUTuao6TaDa7zI7v+q2PUAm3lrMxUp43PFwf2kaQu7FYT9fjS"
crossorigin="anonymous"></script>
<script src="~/js/utils.js"></script>
utils.js:
function AppLoaded() {
microsoftTeams.app.initialize().then(() => {
microsoftTeams.app.notifySuccess();
console.log("app loaded");
});
}
AppLoaded();
config.cshtml have:
private async void SetSelectedResult(SearchModel selectedItem)
{
_selectedItem = selectedItem;
if (_selectedItem != null)
{
var settings = new TeamsInstanceSettings
{
SuggestedDisplayName = _selectedItem.Description,
EntityId = _selectedItem.ID.ToString(),
ContentUrl = contenturl",
WebsiteUrl = websiteurl",
RemoveUrl = removeUrl"
};
await MicrosoftTeams.InitializeAsync();
await MicrosoftTeams.RegisterOnSaveHandlerAsync(settings);
}
} ```
You only need to call microsoftTeams.app.notifySuccess(); in the -actual- tab, not in the config panel. Add a switch in your code somehow (based on page name, or a setting, or however you decide to do it) to only call that from in the main tab.
Update: You are also missing the call to notifyAppLoaded, which is even more important that notifySuccess. See in this document, that you actually need to call notifyAppLoaded to 'hide loading indicator'.

How do you expose methods from NativeScript custom components?

I want to expose a method from a custom component in a NativeScript project. Here is the conceptual setup:
MyComponent.xml
<StackLayout loaded="loaded"> ...UI markup... </StackLayout>
MyComponent.ts
export function loaded(args) { //do stuff };
export function myCustomMethod() { //do more stuff };
MyPage.xml
<Page xmlns:widget="..." loaded="loaded">
<widget:MyComponent id="myComponent" />
</Page>
MyPage.ts
export function loaded(args) {
let page = <Page>args.object;
let widget = page.getViewById("myComponent");
widget.myCustomMethod(); //<--THIS DOES NOT WORK
}
Question: How do I properly expose the myCustomMethod on the custom component so that the hosting page can access and call it via JavaScript?
By default, when trying to get a reference to the custom component on the page, a reference to the custom component's layout container is returned (in this example, a StackLayout).
So, as a workaround, I am doing this:
MyComponent.ts
export function loaded(args) {
let stackLayout = <StackLayout>args.object; //Layout container
stackLayout.myCustomMethod = myCustomMethod; //Attach custom method to StackLayout
}
It's a bit of a hack, but it's working. Is there any better way to expose custom component methods short of creating a super class for the layout container?
UPDATE
To be more accurate, the custom component has instance properties, so the eventual solution would need to support a scenario like...
MyComponent.ts
let isStarted = false;
export function loaded(args){ // do stuff };
export function myCustomMethod() { isStarted = true; };
As the method you want to reuse is not coupled to your customCompoenent, you can create a common file and require it where needed and reuse the exposed methods accordingly. Or you can directly import your MyComponent.ts and reuse its exported methods (not so good idea as you will probably have access to all exposed methods of your MyCompoennt.ts like onLoaded, onNavigationgTo, etc.)
For example:
in your navigator.ts (or if you prefer to your MyComponent.ts)
import frame = require("ui/frame");
export function navigateBack() {
frame.goBack();
}
and then reuse it where needed like this:
MyPage.ts
import navigatorModule = require("navigator");
export function goBack(args: observable.EventData) {
navigatorModule.goBack(); // we are reusing goBack() from navigator.ts
}
Another applicable option is to use MVVM pattern and expose your methods via the binding context.

Nativescript Observable.propertyChangeEvent

I am trying to create some customer formatting on a field (to reproduce a masked text box functionality).
I have an observable and I am capturing the propertyChange Event. My question is: Can I modify the value of the observed property inside the event handler without entering in an infinite loop?
Here is my code:
model.customer.addEventListener(Observable.propertyChangeEvent, function(data) {
if (data.propertyName.toString() === 'homePhone') {
//Here is where I would like to change the value without triggering the event again
//The below code does not seem to be working
data.value = formatPhone(data.value);
}
});
I looked at https://github.com/bthurlow/nativescript-maskedinput, but unfortunately this module does not support databinding.
Thank you. Appreciate your help.
I have tested nativescript-maskedinput in a sample application and I was able to bind text property of this custom view. In regard to that if you add addEventListener and want to update for example TextField text property manually I think that property won't be updated properly. In addition I am attaching some sample code.
main-page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:mi="nativescript-maskedinput" navigatingTo="navigatingTo">
<StackLayout>
<Label text="Tap the button" class="title"/>
<mi:MaskedInput mask="1-999-999-9999? x999" hint="1-555-555-5555" text="{{ masktext }}" placeholder="#" />
<Button text="tapToView" tap="onTap" />
</StackLayout>
</Page>
main-page.js
var observable_1 = require("data/observable"); // Event handler for Page "navigatingTo" event attached in main-page.xml
var newObservable = new observable_1.Observable();
function navigatingTo(args) {
// Get the event sender
var page = args.object;
newObservable.set('masktext', '');
page.bindingContext = newObservable;
}
exports.navigatingTo = navigatingTo;
function onTap(args) {
var newvalue = newObservable.get('masktext');
console.log('newValueget + ' + newvalue);
}
exports.onTap = onTap;
I am not sure if it is possible to override the getter, but if it is not possible you can do this:
function isPhoneFormatted(phone) {
//your algorithm wich return true or false
}
model.customer.addEventListener(Observable.propertyChangeEvent, function(data) {
if (data.propertyName.toString() === 'homePhone') {
//Here is where I would like to change the value without triggering the event again
//The below code does not seem to be working
if (!isPhoneFormatted(data.value)) {
data.value = formatPhone(data.value);
}
}
});
Notice that it is not well tested!

How do I inject a custom component from js in nativescript?

Suppose I create a small component that takes an input and sets a label to show it.
app/components/testComponent/testComponent.xml:
<Label id="someLabel" loaded="onLoad"/>
app/components/testComponent/testComponent.js:
exports.onLoad = args => {
const obj = args.object;
const label = obj.getViewById('someLabel');
label.text = obj.someString || 'no string given';
};
Now I can use this component in any of my pages
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:testComponent="components/testComponent">
<StackLayout>
<testComponent:testComponent someString="hello {N}"/>
</StackLayout>
</Page>
This seems to be the official way to do it and it works. But is there a way to inject this component in the page using javascript only?
Yes, the Declarative UI (i.e. xml) is actually a building system that parses the xml and generates the JS so you don't have to.
So if you wanted to manually do this you would leave your component code alone and you would change your main screen code to be like this:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoad">
<StackLayout id='sl'>
</StackLayout>
</Page>
The first thing you will notice is we gave the Page a loaded event, you have to have somewhere to actually run your code to attach your component to the visual tree. The second thing we did was add to StackLayout an id; this technically isn't actually needed -- you can navigate the NS tree and find the proper StackLayout; but for simplicity adding a ID makes it a lot easier.
So the JS code in your page would be:
var builder = require('ui/builder');
var fs = require('file-system');
exports.onLoad = function(args) {
// Get our current Page that is being loaded
var page = args.object;
// Find our StackLayout
var stackLayout = page.getViewById('sl');
// Load our JS for the component
var path = fs.knownFolders.currentApp().path;
var componentJS = require(path + '/components/testComponent/testComponent.js');
// Actually have the builder build the Component using the XML & JS.
var component = builder.load(path + '/components/testComponent/testComponent.xml', componentJS);
// And add our component to the visual tree
stackLayout.addChild(component);
};
I believe that because you are adding the child in the loaded event that your page loaded event in the child component will be ran, but don't hold me to that one. If it isn't then you can manually run it a the same time you are adding it...
where filepath is a script - or even a class the callback function can create an instance of.
This is as if its loaded at page load and shows in most developer tool consoles.
var uuid='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
var url = filepath + "?" + uuid;//prevent caching - does not work with ajax setup
try {
$.getScript(url, "callbackfunctionname('" + filepath + "')");//getScript callback seems broken so use own
}
catch (e) {
//error handle
}

Does Intellisense work with objects extended with Ext.extend?

I can't get get Intellisense to display any methods other than Object methods when I inherit through Ext.extend(). Is it possible for Intellisense to display the additional methods?
I used the workaround suggested in this SO question to get the namespaces working, so I don't believe that is related to this issue.
Example Code is below:
///<reference path="ext-base.js" />
///<reference path="ext-all.js" />
///<reference path="namespace.js" />
MNS.Production.DetailedGrid = Ext.extend(MNS.commonUI.GridPanel,
{
initComponent: function () {
var columns = this.getColumns();
},
getColumns: function () {
var columns =
//...build columns
},
//....
//....Additional methods, etc.
});
var detailedGrid = new MNS.Production.DetailedGrid();
Although I get intellisense for the MNS.Production.DetailedGrid() command, I don't get any intellisense on the methods of the detailedGrid object, except default methods. How do I get Visual Studio to show these methods?
I have found that although there are two ways to extend an object using ExtJS, the only way that Intellisense will work with your code is if you use the following syntax:
///<reference path="ext-base.js" />
///<reference path="ext-all.js" />
// create constructor for new class
Ext.ResizableConstrained = function(el, config){
Ext.ResizableConstrained.superclass.constructor.call(this, el, config);
};
// extend the base class
Ext.extend(Ext.ResizableConstrained, Ext.Resizable, {
setXConstraint : function(left, right){
// Obtain a reference to parent dd property and setXConstraint
this.dd.setXConstraint(left, right);
},
setYConstraint : function(up, down){
// Obtain a reference to parent dd property and setYConstraint
this.dd.setYConstraint(up, down);
}
});
The /// references need to be at the top of the file.
Have you seen this MSDN post?
MSDN Blog

Resources