I am using xamarin forms to develop my project, and now I am using the Jason Smith Components to refresh my view.
Xamarin.Forms-PullToRefreshLayout
I am using the following code:
scrollControl.RefreshCommand = RefreshCommand;
public ICommand RefreshCommand
{
get {return new Command(async () => await ContentScrollView_Scrolled()); }
}
async Task ContentScrollView_Scrolled()
{
......
}
After I execute the code, the refresh icon still keep spinning. I tried to put the scrollControl.isRefreshing = false at the end. It does not work.
I suspect that when your refresh completes that you are not updating the IsRefreshing property on the UI thread. Try this:
// after your refresh command completes
Device.BeginInvokeOnMainThread (() => {
scrollControl.IsRefreshing = false;
});
Ok well, this solution is kinda weird, and i found it, since the isrefreshing property is false, so i set the isrefreshing to true at the beginning of code, i thought it will auto set to true so i have to manual it myself, in the end it is work like charm
scrollControl.RefreshCommand = RefreshCommand;
public ICommand RefreshCommand
{
get {return new Command(async () => await ContentScrollView_Scrolled()); }
}
async Task ContentScrollView_Scrolled()
{
scrollControl.isRefreshing = true;
......
scrollControl.isRefreshing = false;
}
Related
const callAFunction = () => {
if (AppState.currentState === 'background') {
function1()
}
}
useEffect(()=>{
AppState.addEventListener('change', callAFunction);
},[])
const function1 = () => {
axios.get('/user_login', {
params: {
username: 'john1904',
}
})
.then(function (response) {
if (response.data.status === false) {
function1()
}
})
}
I am using this above function recursively. But as the app goes background function1 is calling again and again as still the function1() i have already called. So i want that function1() call every time as the app goes background. But in Async form as if function1() then it will not call it again.
So i am not able to get how can i do this in when app is in background so it will check if this function is running then don't run it other wise run it.
Right now, if status is false, the only time between requests is the time it takes Axios to call the endpoint. This can be very little time (like a few milliseconds). If you want to poll until you get a status of true, set a timeout for the request.
// ...
.then(function (response) {
if (response.data.status === false) {
setTimeout(function1, 1000);
}
})
The above example will take 1 second between requests. You can adjust 1000 to suit your needs.
See also this question for a common issue with timeouts and React components: Can't perform a React state update on an unmounted component
I have a text field, which has the onChange property, when it detects that the text has a \ n execute a function, the problem is that this function is executed twice, it should be mentioned, that in that function, I clean the text of the TextController.
TextField(
maxLines: null,
controller: codigoController,
autofocus: true,
onChanged: (text) {
if (text.contains('\n')) {
test();
}
},
),
_test() {
print("hello");
codigoController.clear();
}
One of the solution is to use listener on TextController
1. Add Listener
we can add listener at the first time Screen is rendered. Later we need to dispose it as stated by docs
class _AutoCallApiState extends State<AutoCallApi> {
TextEditingController codigoController = TextEditingController();
#override
void initState() {
super.initState();
codigoController.addListener(changesOnField);
}
#override
void dispose() {
codigoController.dispose(); // release unused memory in RAM
super.dispose();
}
2. Handle changes and API Call
Future callApi(String textToSent) async {
await Future.delayed(Duration(seconds: 5));
print("Received OK from API");
codigoController.clear();
}
void changesOnField() {
print("Changes Called");
String text = codigoController.text;
if (text.isNotEmpty) {
print('$text');
if (text.contains('\n')) {
callApi(text);
}
}
}
3. Demo
callApi method only called once
Note : you may see at the demo, it only prints "Saved data to API" once
4. Full Repo
You may look into this repo and build it locally. Github
Maybe you can try this
SchedulerBinding.instance.addPostFrameCallback((it) => {_controller.clear()});
I've had this problem for like 2 weeks. I used Wix's Navigation for navigating around the app. I followed this tutorial for implementing the deeplink/universal link.
I have a base class called BaseScreen where I keep all the deeplink handler like in the tutorial. This BaseScreen would looks like this:
componentDidMount(){
// this handles the case where the app is closed and is launched via Universal Linking.
Linking.getInitialURL()
.then((url) => {
if (url) {
// Alert.alert('GET INIT URL','initial url ' + url)
this.resetStackToProperRoute(url)
}
})
.catch((e) => {})
// This listener handles the case where the app is woken up from the Universal or Deep Linking
Linking.addEventListener('url', this.appWokeUp);
}
componentWillUnmount(){
// Remove the listener
Linking.removeEventListener('url', this.appWokeUp);
}
appWokeUp = (event) => {
// this handles the use case where the app is running in the background and is activated by the listener...
// Alert.alert('Linking Listener','url ' + event.url)
this.resetStackToProperRoute(event.url)
}
resetStackToProperRoute = (url) => {
// grab the trailing portion of the url so we can use that data to fetch proper information from the server
let trailing = url.slice(url.lastIndexOf('=') + 1, url.length)
// go to the desired screen with the trailing token grabbed from the url
this.props.navigator.resetTo({
screen: 'NewPassword',
overrideBackPress: true,
passProps: {
token: trailing
},
animated: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
}
})
}
When the app launch, it'll show the screen LoginScreen which extends the BaseScreen above. After killing the app, click the url from the mail, the app launches LoginScreen first, then it'll redirect to the screen NewPassword, and after everything has done, I'll redirect back to LoginScreen by:
this.props.navigator.resetTo({
screen: 'LoginScreen',
animated: true,
overrideBackPress: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
}
})
But the Linking.getInitialURL() of the LoginScreen still receive the old url, so it'll redirect to NewPassword again, and it's a loop.
I've also tried to pass: passProps: {} option when resetTo the LoginScreen but no luck.
I guess the only way to fix it is to clear the initialUrl manually after everything's done in NewPassword screen. The listener for the BaseScreen should be there because if I don't kill the app (just minimize it), the listener should be running to navigate to NewPassword.
Wix's navigation has a doc for Deeplink, I tried putting method onNavigatorEvent(event) into the BaseScreen but it doesn't get called. I don't know if I miss something.
Thank you for your time. Any idea would be appreciated
Linking.getInitialURL() gives us the same Url when we come back to the same page again, to Overcome this we can do a simple condition of not to call the DeepLink function. Something like...
Step 1: First init a dummyDeepLinkedUrl String .
var dummyDeepLinkedUrl;
Step 2: Check for the condition like, if deeplinkUrl is coming from Linking.getInitialURL() and deeplinkUrl is not equal to the dummyDeepLinkedUrl .
if (url && url != dummyDeepLinkedUrl) {}
Step 3: If not same call the Deeplink Function and assign the deeplinkUrl to dummyDeepLinkedUrl.
this.navigateToRespectivePage(url);
dummyDeepLinkedUrl = url;
Finally this will look like :
Linking.getInitialURL().then(url => {
if (url && url != dummyDeepLinkedUrl) {
this.navigateToRespectivePage(url);
dummyDeepLinkedUrl = url;
}
});
There are two ways to handle URLs that open your app.
If the app is already open, the app is foregrounded and a Linking event is fired You can handle these events with
Linking.addEventListener(url, callback).
If the app is not already open, it is opened and the url is passed in as the initialURL You can handle these events with
Linking.getInitialURL(url) -- it returns a Promise that resolves to
the url, if there is one.
You can read more detail
Here is the example
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
initialised: false
}
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
Linking.addEventListener('url', event => {
console.log('deep link from background', event.url)
})
}
_handleAppStateChange = async (nextAppState) => {
const url = await Linking.getInitialURL();
if (url !== null && !this.state.initialised) {
this.setState({ initialised: true })
console.log('deep link from init app', url)
}
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
Linking.removeEventListener('url')
}
}
I'm currently working on a Ionic/Cordova application, a to-do application. I am using the ngCordova library for an easier use of the cordova plugins. I'm also using the Sqlite plugin by litehelpers (GitHub page).
My problem is about the correct understanding of the JavaScript's promises behavior. I have a Angular service called "Projects", which make use of the SQLite plugin, and a "Tasks" controller which is delegated to it.
angular.module('myapp.services', [])
.factory("Projects", ["$ionicPlatform", "$cordovaSQLite", "$window", "$q",
function($ionicPlatform, $cordovaSQLite, $window, $q) {
return {
// SOME FUNCTIONS..
getCurrentProject: function() {
var q = $q.defer();
$ionicPlatform.ready(function() {
$cordovaSQLite.execute(db,
"SELECT id_project, name FROM projects WHERE active = 1").then(
function(res) {
q.resolve(res.rows.item(0));
}, function(err) {
q.reject(err);
console.error(err.message);
});
});
return q.promise;
}
};
}
]);
I want to make this function return an object, which is the result of my query.
The promises and callback keywords are confusing me.
I've tried a few ways to solve my problem, but when I call (in my controller):
var currentProject = Projects.getCurrentProject();
And then trying to check its values with:
console.log("ID -> " + currentProject.id_project);
console.log("NAME -> " + currentProject.name);
I always get undefined. How do I handle a promise in a Ionic/Cordova Application?
Solved
#bardzusny's answer totally made me realize where i was wrong, thank you all :)
Promises are designed to be chainable with error handling and final behavior (optional), like so:
Projects.getCurrentProject()
.then (project) -> currentProject = project
.catch (err) -> console.error err
.finally () -> wrapUpFn()
So you would need to use .then to make the assignment. Otherwise you're assigning currentProject to the promise object, not the result of the promise object (which is what you want).
EDIT
Now in javascript :)
var currentProject
Projects.getCurrentProject()
.then(function(project) {
currentProject = project
})
.catch(function(err) {
console.error(err)
})
.finally(function() {
wrapUpFn()
})
As an aside, promise objects are great because you can just keep on chaining down the line with .thens, and still keep your error handling and final behaviors clear. This lets you create simple flows that are ordered within the crazy world of javascript.
I have a Sencha Touch 2 project and everything works great in the web browser. No errors in the console, and everything looks good. Once I package it with Phonegap and run it on a mobile device, however, things don't work as well.
I am using ext.device.notification.show in two places in my application. At first, I was doing requires: 'Ext.device.*' and while it worked in web, the app wouldn't run on mobile and eclipse would give me the error message Uncaught TypeError: Cannot read property 'name' of undefined. I switched over to requires: Ext.device.Notification (exact spelling and capitalization) and now the app runs but when I click a button that should create a message box, I get the error Uncaught TypeError: Cannot call method 'confirm' of undefined. The problem is I have no method called confirm. In one case I have a method called confirmItem, but for the second button that should be invoking a message box I have no method remotely close to "confirm."
I'll post one of the controllers below (this one has the confirmItem method):
Ext.define('MyApp.controller.MainController',
{
extend: 'Ext.app.Controller',
requires: ['Ext.device.Notification'],
config:
{
refs:
{
mainView: 'mainview',
btnConfirm: 'mainview button[action=confirmItem]',
},
control:
{
'btnConfirm':
{
tap: 'confirmItem'
},
mainView:
{
onSignOffCommand: 'onSignOffCommand'
}
}
},
// Transitions
getSlideLeftTransition: function ()
{
return {
type: 'slide',
direction: 'left'
};
},
getSlideRightTransition: function ()
{
return {
type: 'slide',
direction: 'right'
};
},
onSignOffCommand: function ()
{
var me = this;
console.log('Signed out.');
loginView = this.getLoginView();
//MainView.setMasked(false);
Ext.Viewport.animateActiveItem(loginView, this.getSlideRightTransition());
},
confirmItem: function ()
{
Ext.device.Notification.show(
{
title: 'Confirm',
message: 'Would you like to Confirm?',
buttons: ['No', 'Yes'],
callback: function (button)
{
if (button == "Yes")
{
MyApp.app.getController('MainController')
.confirmPickup();
}
else
{
console.log('Nope.');
}
}
});
},
confirmPickup: function ()
{
var me = this;
var loginStore = Ext.getStore('LoginStore');
mainView = this.getMainView();
mainView.setMasked(
{
xtype: 'loadmask',
message: ' '
});
if (null != loginStore.getAt(0))
{
var user_id = loginStore.getAt(0).get('id');
var name = loginStore.getAt(0).get('name');
var winner = loginStore.getAt(0).get('winner');
}
if (winner === 1)
{
console.log('success');
}
else
{
console.log('fail');
}
}
});
I only assume this is a problem because whenever I push the button that should be calling confirmItem I get the error. Am I using Ext.device.Notification correctly, or Have I missed something needed to make it work in Phonegap?
I found the solution! Everything was fine from a Sencha Touch point of view in terms of using requires: Ext.device.Notification but some things were missing on the Phonegap side. Specifically, I needed to install the appropriate plugins.
Open a terminal and type: Phonegap local plugin list to see your currently installed plugins. I had none. I went ahead and installed:
org.apache.cordova.device
org.apache.cordova.dialogs
org.apache.cordova.vibration
by using the following reference: http://docs.phonegap.com/en/3.0.0/cordova_device_device.md.html and selecting options from the menu on the left.