My code is in Angular7, using #ngrx/store7, and sometimes I get undefined state (just for a specific state)
It is always the "history" state.
My App State:
export interface IAppState {
router?: RouterReducerState,
delivery: IDeliveryState,
events: IEventsState,
families: IFamiliesState,
googleDrive: IGoogleDriveState,
spinner: ISpinnerState,
user: IUserState,
volunteerConstraints: IVolunteerConstraintsState,
volunteers: IVolunteersState,
history: IHistoryState
}
export const initialAppState: IAppState = {
delivery: initialDeliveryState,
events: initialEventsState,
families: initialFamiliesState,
googleDrive: initialGoogleDriveState,
spinner: initialSpinnerState,
user: initialUserState,
volunteerConstraints: initialVolunteerConstraintsState,
volunteers: initialVolunteersState,
history: initialHistoryState
};
export function getInitialState(): IAppState {
return initialAppState;
}
My history state:
export interface IHistoryState {
familyHistory: Array<FamilyHistoryModel>;
familyHistoryPending: boolean;
routeHistory: Array<RouteHistoryModel>;
routeHistoryPending: boolean;
deliveryHistory: Array<DeliveryHistoryChartModel>;
volunteerRating: Array<VolunteerRatingModel>;
volunteerRatingPending: number;
initiated: boolean;
}
export const initialHistoryState: IHistoryState = {
familyHistory: new Array<FamilyHistoryModel>(),
familyHistoryPending: false,
routeHistory: new Array<RouteHistoryModel>(),
routeHistoryPending: false,
deliveryHistory: new Array<DeliveryHistoryChartModel>(),
volunteerRating: new Array<VolunteerRatingModel>(),
volunteerRatingPending: 0,
initiated: true
};
My History selector:
const selectHistoryState = (state: IAppState) => state.history;
export const selectFamilyHistory = createSelector(
selectHistoryState,
(state: IHistoryState) => state.familyHistory
);
export const selectFamilyHistoryPending = createSelector(
selectHistoryState,
(state: IHistoryState) => state.familyHistoryPending
);
export const selectRouteHistory = createSelector(
selectHistoryState,
(state: IHistoryState) => state.routeHistory
);
export const selectRouteHistoryPending = createSelector(
selectHistoryState,
(state: IHistoryState) => state.routeHistoryPending
);
export const selectDeliveryHistory = createSelector(
selectHistoryState,
(state: IHistoryState) => state.deliveryHistory
);
export const selectVolunteerRating = createSelector(
selectHistoryState,
(state: IHistoryState) => state.volunteerRating
);
export const selectVolunteerRatingPending = createSelector(
selectHistoryState,
(state: IHistoryState) => state.volunteerRatingPending
);
My app reducer:
export const appReducers: ActionReducerMap<IAppState, any> = {
router: routerReducer,
delivery: deliveryReducer,
events: eventsReducer,
families: familiesReducer,
googleDrive: googleDriveReducer,
spinner: spinnerReducer,
user: userReducer,
volunteerConstraints: volunteerConstraintsReducer,
volunteers: volunteersReducer,
history: historyReducer
}
My History reducer:
export function historyReducer(
state = initialHistoryState,
action: actions.HistoryActions): IHistoryState {
switch (action.type) {
case actions.HistoryActionTypes.Initiate:
return {
...state,
initiated: true
};
case actions.HistoryActionTypes.GetFamilyHistory:
return {
...state,
familyHistory: initialHistoryState.familyHistory,
familyHistoryPending:true
};
case actions.HistoryActionTypes.GetFamilyHistorySuccess:
return {
...state,
familyHistory: action.payload,
familyHistoryPending: false
};
case actions.HistoryActionTypes.GetFamilyHistoryFailure:
return {
...state,
familyHistoryPending: false
};
case actions.HistoryActionTypes.GetRouteHistory:
return {
...state,
routeHistory: initialHistoryState.routeHistory,
routeHistoryPending: true
};
case actions.HistoryActionTypes.GetRouteHistorySuccess:
return {
...state,
routeHistory: action.payload,
routeHistoryPending: false
};
case actions.HistoryActionTypes.GetRouteHistoryFailure:
return {
...state,
routeHistoryPending: false
};
case actions.HistoryActionTypes.GetDeliveryHistory:
return {
...state,
deliveryHistory: initialHistoryState.deliveryHistory
};
case actions.HistoryActionTypes.GetDeliveryHistorySuccess:
return {
...state,
deliveryHistory: action.payload
};
case actions.HistoryActionTypes.GetVolunteersRating:
return {
...state,
volunteerRating: initialHistoryState.volunteerRating,
volunteerRatingPending: state.volunteerRatingPending + 1
};
case actions.HistoryActionTypes.GetVolunteersRatingSuccess:
return {
...state,
volunteerRating: action.payload,
volunteerRatingPending: state.volunteerRatingPending - 1
};
case actions.HistoryActionTypes.GetVolunteersRatingFailure:
return {
...state,
volunteerRatingPending: state.volunteerRatingPending - 1 < 0 ? 0 :
state.volunteerRatingPending - 1
};
}
}
At the beginning the history state is undefined (as shown in the picture), and only after I dispatch an history action and change the state in the reducer, it gets initiated.
How do I fix that to be initiated at the beginning as all other states?
packge.json:
"dependencies": {
"#agm/core": "^1.0.0-beta.3",
"#agm/js-marker-clusterer": "^1.0.0-beta.3",
"#angular-mdl/core": "^6.0.1",
"#angular-mdl/datepicker": "^6.0.0",
"#angular-mdl/expansion-panel": "^6.0.0",
"#angular-mdl/popover": "^6.0.0",
"#angular-mdl/select": "^6.0.0",
"#angular/animations": "~7.1.0",
"#angular/cdk": "^7.2.2",
"#angular/common": "~7.1.0",
"#angular/compiler": "~7.1.0",
"#angular/core": "~7.1.0",
"#angular/forms": "~7.1.0",
"#angular/http": "^7.2.2",
"#angular/material": "^7.2.2",
"#angular/platform-browser": "~7.1.0",
"#angular/platform-browser-dynamic": "~7.1.0",
"#angular/router": "~7.1.0",
"#ngrx/effects": "^7.1.0",
"#ngrx/router-store": "^7.1.0",
"#ngrx/schematics": "^7.1.0",
"#ngrx/store": "^7.1.0",
"#ngrx/store-devtools": "^7.1.0",
"#ngx-translate/core": "^10.0.2",
"#ngx-translate/http-loader": "^3.0.1",
"#swimlane/ngx-charts": "^10.0.0",
"angular-2-local-storage": "^2.0.0",
"angular2-moment": "^1.9.0",
"angular2localization": "^1.4.2",
"aos": "^2.3.4",
"core-js": "^2.5.4",
"crypto-js": "^3.1.9-1",
"hammer-timejs": "^1.1.0",
"hammerjs": "^2.0.8",
"js-marker-clusterer": "^1.0.0",
"jssha": "^2.3.1",
"lodash": "^4.17.4",
"lodash-es": "^4.17.4",
"moment": "^2.20.1",
"ngrx-store-freeze": "^0.1.9",
"ngrx-store-localstorage": "^0.1.8",
"ngrx-store-logger": "^0.1.8",
"ngx-chips": "^1.4.5",
"ngx-device-detector": "^1.3.0",
"ngx-gallery": "^5.6.2",
"ngx-infinite-scroll": "^0.5.1",
"rxjs": "~6.3.3",
"rxjs-compat": "^6.0.0",
"sha.js": "^2.4.11",
"tslib": "^1.9.0",
"videogular2": "^6.3.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"#angular-devkit/build-angular": "~0.11.0",
"#angular/cli": "~7.1.4",
"#angular/compiler-cli": "~7.1.0",
"#angular/language-service": "~7.1.0",
"#types/core-js": "^2.5.0",
"#types/googlemaps": "^3.30.16",
"#types/jasmine": "~2.8.8",
"#types/jasminewd2": "~2.0.3",
"#types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.1.6"
}
Looks to me like you are missing default case in your history reducer.
switch(...) {
case: ...
case: ...
default: {
return state;
}
}
Related
So I have made a NextJS App as a frontend and backend server, it works fine when running it locally (by running yarn dev or yarn build then yarn start), but not working on Docker (using the content of Dockerfile from the official Github project). Found out that the whole backend server is not working at all (knew it because I tried to manually access the API outside from the frontend and it all returned NextJS's 404 Not Found Page), what did I do wrong?
Here's the next.config.js:
module.exports = {
output: 'standalone',
async redirects() {
return [
{
source: '/',
destination: `/${process.env.COUNTRY_CODE}/login`,
permanent: false,
basePath: false,
},
{
source: '/login',
destination: `/${process.env.COUNTRY_CODE}/login`,
permanent: false,
basePath: false,
},
]
},
basePath: `/${process.env.COUNTRY_CODE}`,
trailingSlash: true,
env: {
CRMNX_COUNTRY_CODE: process.env.COUNTRY_CODE,
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
}
};
my package.json:
{
"name": "myapp",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "node server.js",
"watch": "nodemon server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},
"dependencies": {
"#emotion/react": "^11.10.5",
"#emotion/styled": "^11.10.5",
"#headlessui/react": "^1.6.4",
"#heroicons/react": "^1.0.6",
"#mui/icons-material": "^5.10.9",
"#mui/material": "^5.10.12",
"#mui/x-date-pickers": "^5.0.8",
"axios": "^0.27.2",
"bootstrap": "^5.2.2",
"cookie-session": "^2.0.0",
"csurf": "^1.11.0",
"date-format": "^4.0.14",
"dayjs": "^1.11.6",
"express": "^4.18.2",
"express-rate-limit": "^6.4.0",
"express-session": "^1.17.3",
"formik": "^2.2.9",
"iron-session": "^6.3.1",
"jose": "^4.11.1",
"jsonwebtoken": "^8.5.1",
"libphonenumber-js": "^1.10.6",
"moment": "^2.29.4",
"mysql2": "^2.3.3",
"next": "^13.0.6",
"next-auth": "^4.17.0",
"node-cache": "^5.1.2",
"pure-react-carousel": "^1.30.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-device-detect": "^2.2.2",
"react-hook-form": "^7.31.3",
"react-phone-input-2": "^2.15.1",
"sequelize": "^6.25.3",
"sweetalert2": "^11.4.8",
"yup": "^0.32.11"
},
"devDependencies": {
"#babel/preset-react": "^7.18.6",
"autoprefixer": "^10.4.7",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"eslint": "8.20.0",
"eslint-config-next": "^13.0.6",
"nodemon": "^2.0.20",
"postcss": "^8.4.14",
"prettier": "2.7.1",
"react-select": "^5.5.6",
"sequelize-cli": "^6.5.2",
"tailwindcss": "^3.0.24"
}
}
and my server.js:
const next = require("next");
require('dotenv').config()
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== "production";
const hostname = process.env.APP_HOSTNAME || "localhost";
const app = next({
dev,
hostname,
port,
});
const handle = app.getRequestHandler();
app.prepare().then(() => {
const csrf = require("csurf");
const cookieSession = require("cookie-session");
const server = express();
server.disable("x-powered-by");
server.use(express.json());
server.use(express.urlencoded({ extended: true }));
server.use(
cookieSession({
name: "myapp.sid",
keys: [process.env.APP_KEY],
maxAge: 24 * 60 * 60 * 1000,
})
);
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
});
// server.use('/api', limiter)
const router = require("./server/route");
server.use(`/${process.env.COUNTRY_CODE}/api`, router);
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
I am trying to get the direction in my nativescript app using the CLHeading trueHeading but it always returns as null.
locationservice.ts
import { Injectable } from "#angular/core";
#Injectable()
export class LocationService {
private iosLocManager: CLLocationManager;
private locManagerDelegate: LocationMangerDelegate;
constructor() {
this.iosLocManager = new CLLocationManager();
this.locManagerDelegate = new LocationMangerDelegate();
this.iosLocManager.desiredAccuracy = 3;
this.iosLocManager.distanceFilter = 0.1;
this.iosLocManager.delegate = this.locManagerDelegate;
}
getDirection(): number {
return this.locManagerDelegate.currentHeading;
}
startUpdatingHeading(): void {
this.locManagerDelegate.currentHeading = null;
this.iosLocManager.startUpdatingHeading();
}
stopUpdatingHeading(): void {
this.iosLocManager.stopUpdatingHeading();
}
}
export class LocationMangerDelegate extends NSObject implements CLLocationManagerDelegate {
public static ObjCProtocols = [CLLocationManagerDelegate];
currentHeading: number;
locationManagerDidUpdateHeading(locationManager: CLLocationManager, heading: CLHeading): void {
this.currentHeading = heading.trueHeading;
}
}
Component
constructor(private locationService: LocationService) {
this.locationService.startUpdatingHeading();
}
GetDirection(): void {
let direction = this.locationService.getDirection();
}
how can I get the value of trueHeading or how can I get the direction in NativeScript IOS ?
Package.json
{
"dependencies": {
"#angular/animations": "~13.2.0",
"#angular/common": "~13.2.0",
"#angular/compiler": "~13.2.0",
"#angular/core": "~13.2.0",
"#angular/forms": "~13.2.0",
"#angular/http": "7.2.16",
"#angular/platform-browser": "~13.2.0",
"#angular/platform-browser-dynamic": "~13.2.0",
"#angular/router": "~13.2.0",
"base-64": "^0.1.0",
"cross-env": "^5.2.0",
"lodash": "^4.17.11",
"#nativescript/angular": "^13.0.0",
"#nativescript/appversion": "2.0.0",
"#nativescript/camera": "5.0.10",
"nativescript-couchbase": "^1.0.18",
"#nativescript/email": "2.0.5",
"#nativescript/geolocation": "8.0.2",
"nativescript-phone": "3.0.3",
"nativescript-screen-orientation": "^2.0.0",
"nativescript-theme-core": "~1.0.4",
"reflect-metadata": "~0.1.13",
"rxjs": "~7.5.0",
"rxjs-compat": "^6.4.0",
"utf8": "^3.0.0",
"zone.js": "~0.11.5",
"#nativescript/core": "~8.2.0"
},
"devDependencies": {
"#angular/compiler-cli": "~13.2.0",
"#nativescript/schematics": "~0.5.0",
"#ngtools/webpack": "~13.2.0",
"#nativescript/webpack": "5.0.6",
"#angular-devkit/build-angular": "~13.2.0",
"#nativescript/ios": "8.2.1",
"#nativescript/types": "8.1.1",
"typescript": "~4.5.5"
},
"readme": "NativeScript Application",
"main": "./src/main.ts"
}
Reference.d.ts
/// <reference path="./node_modules/#nativescript/types/index.d.ts" />
Info.plist
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>magnetometer</string>
<string>gps</string>
<string>location-services</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Description</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Description</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Description</string>
How can i get the direction from the CLHeading trueHeading? . it always returns null ? what am I missing?
Check the docs:
This property contains a valid value only if location updates are also enabled for the corresponding location manager object. Because the position of true north is different from the position of magnetic north on the Earth’s surface, Core Location needs the current location of the device to compute the value of this property.
I am using ionic with capacitor and angularfire. It works fine but does not work on ios. On ios I don't see any error - just the firebase call that never returns anything. Below is my package.json and other relevant info.
ionic.info:
ionic info
Ionic:
Ionic CLI : 6.18.1 (/usr/local/lib/node_modules/#ionic/cli)
Ionic Framework : #ionic/angular 5.9.1
#angular-devkit/build-angular : 12.1.4
#angular-devkit/schematics : 12.1.4
#angular/cli : 12.1.4
#ionic/angular-toolkit : 4.0.0
Capacitor:
Capacitor CLI : 3.3.2
#capacitor/android : 3.3.2
#capacitor/core : 3.3.2
#capacitor/ios : 3.3.2
Cordova:
Cordova CLI : 10.0.0 (cordova-lib#10.1.0)
Cordova Platforms : android broken, ios 5.1.1
Cordova Plugins : no whitelisted plugins (0 plugins total)
Utility:
cordova-res (update available: 0.15.4) : 0.15.3
native-run : 1.5.0
System:
ios-deploy : 1.9.4
ios-sim : ios-sim/9.0.0 darwin-x64 node-v14.17.0
NodeJS : v14.17.0 (/usr/local/bin/node)
npm : 7.24.0
OS : macOS Monterey
Xcode : Xcode 13.1 Build version 13A1030d
Package.json:
"dependencies": {
"#angular/common": "~12.1.1",
"#angular/core": "~12.1.1",
"#angular/fire": "^7.2.0",
"#angular/forms": "~12.1.1",
"#angular/platform-browser": "~12.1.1",
"#angular/platform-browser-dynamic": "~12.1.1",
"#angular/router": "~12.1.1",
"#capacitor/android": "^3.3.2",
"#capacitor/app": "^1.0.6",
"#capacitor/browser": "^1.0.6",
"#capacitor/core": "3.3.2",
"#capacitor/device": "^1.1.0",
"#capacitor/haptics": "^1.1.3",
"#capacitor/ios": "^3.3.2",
"#capacitor/keyboard": "^1.1.3",
"#capacitor/splash-screen": "^1.1.6",
"#capacitor/status-bar": "^1.0.6",
"#capacitor/storage": "^1.2.3",
"#ionic-native/app-version": "^5.36.0",
"#ionic-native/barcode-scanner": "^5.36.0",
"#ionic-native/call-number": "^5.36.0",
"#ionic-native/camera": "^5.36.0",
"#ionic-native/camera-preview": "^5.36.0",
"#ionic-native/contacts": "^5.36.0",
"#ionic-native/email-composer": "^5.36.0",
"#ionic-native/in-app-browser": "^5.36.0",
"#ionic-native/in-app-purchase": "^5.36.0",
"#ionic-native/in-app-purchase-2": "^5.36.0",
"#ionic-native/social-sharing": "^5.36.0",
"#ionic/angular": "^5.5.2",
"#ngx-translate/core": "^11.0.1",
"#ngx-translate/http-loader": "^4.0.0",
"call-number": "^1.0.1",
"cordova-plugin-app-version": "^0.1.12",
"cordova-plugin-camera": "^6.0.0",
"cordova-plugin-camera-preview": "^0.12.3",
"cordova-plugin-contacts": "^3.0.1",
"cordova-plugin-email-composer": "^0.10.0",
"cordova-plugin-inappbrowser": "^5.0.0",
"cordova-plugin-inapppurchase": "^1.2.0",
"cordova-plugin-purchase": "^10.6.1",
"cordova-plugin-x-socialsharing": "^6.0.3",
"firebase": "^9.5.0",
"forcejs": "^2.2.1",
"phonegap-plugin-barcodescanner": "^8.1.0",
"rxjs": "~6.6.0",
"tslib": "^2.2.0",
"xcode": "^3.0.1",
"xml-js": "^1.6.11",
"zone.js": "~0.11.4"
},
"devDependencies": {
"#angular-devkit/build-angular": "~12.1.1",
"#angular-eslint/builder": "~12.0.0",
"#angular-eslint/eslint-plugin": "~12.0.0",
"#angular-eslint/eslint-plugin-template": "~12.0.0",
"#angular-eslint/template-parser": "~12.0.0",
"#angular/cli": "~12.1.1",
"#angular/compiler": "~12.1.1",
"#angular/compiler-cli": "~12.1.1",
"#angular/language-service": "~12.0.1",
"#capacitor/cli": "3.3.2",
"#ionic/angular-toolkit": "^4.0.0",
"#types/jasmine": "~3.6.0",
"#types/jasminewd2": "~2.0.3",
"#types/node": "^12.11.1",
"#typescript-eslint/eslint-plugin": "4.16.1",
"#typescript-eslint/parser": "4.16.1",
"es6-promise-plugin": "^4.2.2",
"eslint": "^7.6.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsdoc": "30.7.6",
"eslint-plugin-prefer-arrow": "1.2.2",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.2",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"typescript": "~4.2.4"
}
app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { AngularFireModule } from '#angular/fire/compat';
import { AngularFireDatabaseModule } from '#angular/fire/compat/database';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { FormsModule } from '#angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent,
],
entryComponents: [
],
imports: [
AngularFireModule.initializeApp(firebaseConfig),
AngularFireDatabaseModule,
BrowserModule,
HttpClientModule,
IonicModule.forRoot(),
AppRoutingModule,
FormsModule,
],
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
The code that never executes and no error in catch block is below. Same works just fine on android:
const resp:any = await this.dataSvc.getSeedConfig()
console.log("seed config is::" + JSON.stringify(resp))
So the above code does not go to the catch block and does not print the seed config on ios. Works fine on android.
Also on ios it works using live reload using
ionic capacitor run ios -l --external
so if i run it using xcode then it does not work. not sure what is blocking in that specific case
Already answered this here https://stackoverflow.com/a/70594992/5701521 but could be useful to answer it here as well.
Been struggling a lot with this issue too but I managed to fix it. For those who need help here's my code.
You can delete all Firebase related imports from app.module.ts since this solution only uses Firebase.
The packages rxfire and #angular/fire can be removed from your package.json. The only dependency I have is "firebase": "^9.6.1".
I used observables for the getObject and list functions since that's what I'm used to and I didn't want to rewrite my original code.
import { Injectable } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { environment } from '#environment';
import { initializeApp } from 'firebase/app';
import { Auth, getAuth, indexedDBLocalPersistence, initializeAuth, signInWithCustomToken } from 'firebase/auth';
import { Database, getDatabase, onValue, orderByChild, query, ref } from 'firebase/database';
import { Observable, Observer, from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FirebaseService {
private readonly database: Database;
private readonly auth: Auth;
constructor() {
const firebaseApp = initializeApp(environment.firebase);
if (Capacitor.isNativePlatform()) {
initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
});
}
this.database = getDatabase(firebaseApp);
this.auth = getAuth(firebaseApp);
}
connectFirebase(firebaseToken) {
return from(signInWithCustomToken(this.auth, firebaseToken));
}
disconnectFirebase() {
return from(this.auth.signOut());
}
getObject<T>(path: string): Observable<T> {
return new Observable((observer: Observer<T>) => {
const dbRef = ref(this.database, path);
const listener = onValue(dbRef, snapshot => {
const data = snapshot.val();
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
public list<T>(path: string, orderChildBy?: string): Observable<Array<T>> {
return new Observable<Array<T>>((observer: Observer<Array<T>>) => {
const dbRef = ref(this.database, path);
const dbReference = !orderChildBy ? dbRef : query(dbRef, orderByChild(orderChildBy));
const listener = onValue(dbReference, snapshot => {
const data = Object.values<T>(snapshot.val() || {});
console.log(path, data);
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
}
For those who can't see the error message thrown by firebase try the following command in your Safari console to see the error.
window.location.reload()
Try to downgrade your angularfire to v7.0 it might work with firebase v9.
in my case i folow this link: https://github.com/angular/angularfire/issues/3087#issuecomment-990599459
I was also having this issue when building for an iOS emulator and actual iOS device, but adding this to the app.component.ts worked for me.
import { Component } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { initializeApp } from 'firebase/app';
import { indexedDBLocalPersistence, initializeAuth } from 'firebase/auth';
import { environment } from 'src/environments/environment';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent {
constructor() {
const app = initializeApp(environment.firebase);
if (Capacitor.isNativePlatform) {
initializeAuth(app, {
persistence: indexedDBLocalPersistence
});
}
}
}
This is where I found the fix and a sample code
Detox is not working with Expo SDK 36?
Debugging reveals that it's hanging on await device.launchApp() in reloadApp() within detox-expo-helpers.
I've gone through the setup highlighted in:
Is it actually possible to make Detox/Jest tests pass with a React Native app running with Expo?
Example of my test:
const { reloadApp } = require('detox-expo-helpers');
describe('Example', () => {
beforeEach(async () => {
await reloadApp();
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
});
package.json
"scripts": {
"e2e": "detox test --configuration ios.sim",
},
"devDependencies": {
"babel-eslint": "^10.0.3",
"babel-preset-expo": "^7.0.0",
"detox": "^15.2.2",
"detox-expo-helpers": "^0.6.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-prettier": "^3.1.1",
"expo-detox-hook": "^1.0.10",
"jest-expo": "^35.0.0",
"pusher-js-mock": "^0.2.0",
"react-native-testing-library": "^1.12.0",
"react-test-renderer": "^16.12.0"
},
"private": true,
"detox": {
"configurations": {
"ios.sim": {
"binaryPath": "bin/Exponent.app",
"type": "ios.simulator",
"name": "iPhone 11"
}
},
"test-runner": "jest"
}
Even if I bypass the hanging promise via
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
beforeEach(async () => {
reloadApp();
await timeout(12000);
});
It hangs on await expect(element(by.id('welcome'))).toBeVisible();
What am I doing wrong?
I have an ionic 3 app.
On a page I have a form with some fields.
<form>
<ion-item>
<ion-label>First item</ion-label>
<ion-input type="text" [(ngModel)]="title" name="title"></ion-input>
</ion-item>
... some more simple fields ...
<ion-item>
<ion-label>Item below keyboard region</ion-label>
<ion-textarea [(ngModel)]="description" name="description"></ion-textarea>
</ion-item>
<button ion-button type="submit" block>Add Todo</button>
</form>
When I tab the first, the keyboard is shown and the input item is properly focussed, that is: shows a blinking caret.
Though, when I click a field at a position below the area needed to show the keyboard, I do not get a caret, although the field is actually focussed. When I type, the karakters are put in the field.
The main difference is that when clicking on the lower field, the form is shifted upwards when the keyboard shows.
How to fix this?
I am running the app on an iPad 2017, iOS 11.2.2.
package.json:
{
"name": "my app",
"version": "1.0.1",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build --release",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve"
},
"dependencies": {
"#angular/animations": "5.0.0",
"#angular/common": "5.0.0",
"#angular/compiler": "5.0.0",
"#angular/compiler-cli": "5.0.0",
"#angular/core": "5.0.0",
"#angular/forms": "5.0.0",
"#angular/http": "5.0.0",
"#angular/platform-browser": "5.0.0",
"#angular/platform-browser-dynamic": "5.0.0",
"#ionic-native/app-version": "^4.5.2",
"#ionic-native/calendar": "^4.3.2",
"#ionic-native/call-number": "^4.4.2",
"#ionic-native/camera": "^4.3.2",
"#ionic-native/core": "4.3.0",
"#ionic-native/date-picker": "^4.4.2",
"#ionic-native/file": "^4.4.2",
"#ionic-native/in-app-browser": "^4.3.3",
"#ionic-native/keyboard": "^4.4.2",
"#ionic-native/media-capture": "^4.4.0",
"#ionic-native/native-page-transitions": "^4.3.2",
"#ionic-native/splash-screen": "4.3.0",
"#ionic-native/status-bar": "4.3.0",
"#ionic/pro": "^1.0.9",
"#ionic/storage": "2.0.1",
"#ngx-translate/core": "^9.1.0",
"#ngx-translate/http-loader": "^2.0.1",
"call-number": "^1.0.1",
"com.telerik.plugins.nativepagetransitions": "^0.6.5",
"cordova-ios": "^4.5.4",
"cordova-plugin-app-version": "^0.1.9",
"cordova-plugin-calendar": "^4.6.0",
"cordova-plugin-camera": "^2.4.1",
"cordova-plugin-compat": "^1.2.0",
"cordova-plugin-datepicker": "^0.9.3",
"cordova-plugin-device": "^1.1.7",
"cordova-plugin-file": "^5.0.0",
"cordova-plugin-file-transfer": "^1.7.0",
"cordova-plugin-inappbrowser": "^1.7.2",
"cordova-plugin-ionic-webview": "^1.1.16",
"cordova-plugin-media-capture": "^1.4.3",
"cordova-plugin-privacyscreen": "^0.4.0",
"cordova-plugin-splashscreen": "^4.1.0",
"cordova-plugin-statusbar": "^2.3.0",
"cordova-plugin-whitelist": "^1.3.3",
"cordova-windows": "^5.0.0",
"intl": "^1.2.5",
"ionic-angular": "3.9.2",
"ionic-plugin-keyboard": "^2.2.1",
"ionicons": "3.0.0",
"mx.ferreyra.callnumber": "0.0.2",
"ng2-datepicker": "^2.2.1",
"plist": "^2.1.0",
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"
},
"devDependencies": {
"#ionic/app-scripts": "3.1.0",
"cors": "^2.8.4",
"typescript": "2.4.2",
"ws": "3.3.2"
},
"description": "An Ionic project",
"cordova": {
"plugins": {
"com.telerik.plugins.nativepagetransitions": {},
"cordova-plugin-camera": {
"CAMERA_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-calendar": {
"CALENDAR_USAGE_DESCRIPTION": "This app uses your calendar to plan sessions."
},
"cordova-plugin-privacyscreen": {},
"ionic-plugin-keyboard": {},
"cordova-plugin-whitelist": {},
"cordova-plugin-device": {},
"cordova-plugin-splashscreen": {},
"cordova-plugin-ionic-webview": {},
"cordova-plugin-inappbrowser": {},
"cordova-plugin-media-capture": {
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-datepicker": {},
"mx.ferreyra.callnumber": {},
"cordova-plugin-statusbar": {},
"call-number": {},
"cordova-plugin-file": {
"PHOTOLIBRARY_USAGE_DESCRIPTION": "This allows",
"PHOTOLIBRARY_ADD_USAGE_DESCRIPTION": "This allows",
"FILE_USAGE_DESCRIPTION": "This app uses your files to upload on sessions.",
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " "
},
"cordova-plugin-app-version": {}
},
"platforms": [
"windows",
"ios"
]
}
}
app.module.ts:
...
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
IonicModule.forRoot(MyApp, {scrollAssist: false, autoFocusAssist: 'delay'})
],
in app.component.ts:
this.platform.ready().then(() => {
console.log('Platform is ready!');
this.keyboard.disableScroll(false);
...
thanks!
I have this hackish solution, but I guess there have to be a better one. But because of the comment of user2158259, I'll post it anyway. Please don't punish me for posting it ;-)
1) Remove packages/plugins if you have them:
ionic cordova plugin remove ionic-plugin-keyboard
npm uninstall ionic-plugin-keyboard --save
npm uninstall #ionic-native/keyboard --save
2) Remove any references in app.module.ts such as
this.keyboard.disableScroll(true);
3) create a sub-folder in the app-folder and add these two files:
device.service.ts
The following code is a subset of my device.service.ts
import {ApplicationRef, EventEmitter, Injectable} from '#angular/core';
#Injectable()
export class DeviceService {
public onTick: EventEmitter<string> = new EventEmitter();
private tickValue = new Date().getTime();
constructor(public appRef: ApplicationRef) {
window.addEventListener('onresize', () => {
console.log('DeviceService: on resize');
this.doTick();
setTimeout(() => {
this.doTick();
}, 100);
}, false);
window.addEventListener('transitionend', () => {
console.log('transition ended');
this.doTick();
}, false);
this.tickValue = new Date().getTime();
setTimeout(() => {
this.doTick();
});
}
/**
* getTickValue() returns a different value when something changed (orientation, keyboard up/down).
* by adding this to the screen, the Ionic caret will be adjusted properly
*/
getTickValue(): string {
return this.tickValue + ' ' + window.innerWidth + ' ' + window.innerHeight + ' ' + window.orientation;
}
doTick(): void {
this.tickValue = new Date().getTime();
this.onTick.emit(String(this.tickValue));
this.appRef.tick();
}
}
kb-scroll.ts
import {ApplicationRef, Directive, ElementRef, HostListener, Renderer2} from '#angular/core';
import {DeviceService} from './device.service';
#Directive({
selector: '[kb-scroll]' // Attribute selector
})
export class KbScrollDirective {
constructor(public appRef: ApplicationRef,
public elm: ElementRef,
public renderer: Renderer2,
public device: DeviceService) {
}
#HostListener('click', ['$event'])
onClick($event) {
let elmClickedY;
let scrollContent: HTMLElement;
if ('TEXTAREA' === this.elm.nativeElement.tagName) {
scrollContent = $event.toElement;
elmClickedY = $event.offsetY;
} else if ('ION-CONTENT' === this.elm.nativeElement.tagName) {
// calculate Y offset between click and top of scroll-content area
scrollContent = this.elm.nativeElement.querySelector('.scroll-content');
if (scrollContent) {
// $event.offsetY is most likely small offset in clicked input field in scroll content
// calculate the offsetY opposed to the container (div.scroll-content)
let clickScreenY = $event.toElement.getBoundingClientRect().top + $event.offsetY;
let scrollContentScreenY = scrollContent.getBoundingClientRect().top;
elmClickedY = clickScreenY - scrollContentScreenY;
} else {
console.warn('KbScrollDirective: could not find .scroll-content div in ', this.elm.nativeElement);
}
} else {
console.warn('KbScrollDirective: Can\'t handle ', this.elm.nativeElement.tagName);
}
//TODO: OK to 'RE-ASSIGN' window.onresize ?
window.onresize = () => {
if (scrollContent) {
setTimeout(() => {
let elmHeight = scrollContent.clientHeight;
if (elmClickedY > elmHeight) {
let toScroll = elmClickedY - elmHeight;
scrollContent.scrollTop += toScroll + 40;
this.device.doTick();
}
}, 100);
}
}
}
}
4) Add this service and directive to you module
5) Use the kb-scroll directive on your <ion-content> HTML tag:
<ion-content kb-scroll>
... your form here ...
</ion-content>
6) now comes the really dirty part (and it will become clear why we need the service).
It seems that the repaint of the Ionic caret is triggered when something changes on the screen, so we need to force that.
I added a <span> to my HTML template that holds the root <ion-nav> tag:
app.html
<!-- invisible ticker, needed to get Keyboard caret into place -->
<div style="position: absolute; top:-100px">{{tickValue}}</div>
<!-- Disable swipe-to-go-back because it's poor UX to combine STGB with side menus -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false" class="root-content"></ion-nav>
7) Add this code in your app.component.ts:
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
...
tickValue: string;
constructor(...,
public device: DeviceService) {
this.device.onTick.subscribe(tickValue => {
this.tickValue = tickValue;
});
}
So whenever the DeviceService#tickValue get's updated, this subscriber will update the app component's tick-value, causing a repaint of the screen, although the tick value is rendered outside the visible area of the screen. This seems to cause the Ionic caret to be positioned correctly.
Bonus: this also works for a textarea that takes more place than half the screen (= screen height - keyboard height). Just add the kb-scroll directive to your <textarea> HTML tag.
Please note this is a very hackish solution.
Any comments/improvements are really welcome!