Nativescript Listview showing nothing - angular-ui-router

I have a minimalist Nativescript hello world application. It has just 2 screens, 'Login' and 'List'.
Here is the flow:
User presses the login button in first screen.
Second screen is shown, which is suppose to show the list of names; but which shows nothing.
If I type even a single character in text field, it shows the list.
I have been scouring over Stack Overflow and Github, it seems the problem others are facing is a bit complicated, like "list not getting updated after http call" etc.
So it seems I am making some obvious mistake here, because this basic scenario should 'just work'. But that small mistake alludes me.
Here is my login file:
/*login.component.ts*/
import { Component } from "#angular/core";
import firebase = require("nativescript-plugin-firebase");
import * as Rx from "rxjs";
import { MyFireLoginService } from '../../mobile-fire/fire-login.service'
import { Router } from "#angular/router";
import { Observable } from "rxjs/Observable";
#Component({
selector: "login",
templateUrl: './pages/login/login.component.html',
styleUrls: ['./pages/login/login.component.css']
})
export class LoginComponent {
email: string = 'user1#site.com';
password: string = 'password';
user: Observable<any>;
constructor(private fl: MyFireLoginService, private router: Router) {
this.email = 'user1#site.com';
this.password = 'password';
this.user = fl.authState;
this.watchUser();
}
watchUser(): void {
this.user.subscribe((usr) => {
if (usr.uid) {
this.router.navigate(["/list"]);
}
})
}
login(): void {
this.fl.loginWithEmail(this.email, this.password)
}
}
and list file:
import { Component } from "#angular/core";
import firebase = require("nativescript-plugin-firebase");
import * as Rx from "rxjs";
import { MyFireLoginService } from '../../mobile-fire/fire-login.service'
import { Router } from "#angular/router";
import { Observable } from "rxjs/Observable";
#Component({
selector: "list",
templateUrl: './pages/list/list.component.html',
styleUrls: ['./pages/list/list.component.css']
})
export class ListComponent {
items: any[] = [];
user: Observable<any>;
constructor(private fl: MyFireLoginService,
private router: Router) {
this.user = fl.authState;
this.watchUser();
this.items = [{
name: 'aks1'
}, {
name: 'aks2'
}, {
name: 'aks3'
}]
}
watchUser(): void {
this.user.subscribe((usr) => {
if (!usr.uid) {
this.router.navigate(["/"]);
}
})
}
private textChanged(e: any): void {
console.log(e.value);
// console.dir(this.items);
}
logout() {
this.fl.logout()
}
}
list.component.html
<ActionBar title="List" class="action-bar"></ActionBar>
<StackLayout>
<Button class="submit-button" text="Logout" (tap)="logout()"></Button>
<TextField hint="empty field for testing.." (textChange)="textChanged($event)"></TextField>
<Label *ngFor="let item of items" [text]="item.name"></Label>
<!-- <GridLayout>
<ListView id="list-of-items" [items]="items" class="small-spacing">
<ng-template let-item="item">
<Label [text]="item.name" class="medium-spacing"></Label>
</ng-template>
</ListView>
</GridLayout> -->
</StackLayout>
login.component.html
<ActionBar title="Login" class="action-bar"></ActionBar>
<StackLayout>
<Button class="submit-button" text="Sign in" (tap)="login()"></Button>
<Label text="Hello world"></Label>
</StackLayout>
EDIT: Here are the log for the life-cycle events of two components:
JS: LOGIN:ngDoCheck called
JS: LOGIN:ngAfterContentChecked called
JS: LOGIN:ngAfterViewChecked called
JS: Auth state changed.
JS: a <==== This is me typing 'a' in the textbox of LIST component
JS: LOGIN:ngDoCheck called
JS: LOGIN:ngAfterContentChecked called
JS: LOGIN:ngAfterViewChecked called
JS: LIST:ngOnInit called
JS: LIST:ngDoCheck called
JS: LIST:ngAfterContentInit called
JS: LIST:ngAfterContentChecked called
JS: LIST:ngAfterViewChecked called
What I find strange is that List component's init is not called until I type 'a' in the textbox inside List component. Also, LOGIN component's life-cycle events are called even after is it gone out of view.
Update1: I followed the tutorial on nativescript.org. The code there seems to be working fine. I suspect, there is something wrong with inclusion of firebase plugin. Will update when I know more.
Update2: I stubbed parts of firebase api I am using, it worked fine with dummy APIs.

Related

Can't get Firebase emulators to work with AngularFire 7

Good talk yesterday at the Firebase Summit about emulators! I was able to get the Functions emulator to work with AngularFire 6. I can't get the Firestore emulator or the Functions emulator to work with AngularFire 7. Here's my app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { initializeApp,provideFirebaseApp } from '#angular/fire/app';
import { environment } from '../environments/environment';
import { provideFirestore,getFirestore } from '#angular/fire/firestore';
import { USE_EMULATOR as USE_FIRESTORE_EMULATOR } from '#angular/fire/compat/functions';
import { USE_EMULATOR as USE_FUNCTIONS_EMULATOR } from '#angular/fire/compat/functions';
import { FormsModule } from '#angular/forms';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideFirestore(() => getFirestore()),
],
providers: [
{ provide: USE_FIRESTORE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 8080] : undefined },
{ provide: USE_FUNCTIONS_EMULATOR, useValue: environment.useEmulators ? ['localhost', 5001] : undefined }
],
bootstrap: [AppComponent]
})
export class AppModule { }
There's a smell here. I'm initializing Firebase using AngularFire 7 but I'm importing the emulator from AngularFire 6.1.0. Firebase can be initialized with AngularFire 6 or AngularFire 7 but not both, i.e., you can't mix AngularFire 6 and 7.
How do I import the emulators without using AngularFire 6?
In environments.ts I made a property useEmulators:
export const environment = {
firebase: {
projectId: 'my-awesome-project',
appId: '1:234567890:web',
storageBucket: 'my-awesome-project.appspot.com',
apiKey: 'ABCdef',
authDomain: 'my-awesome-project.firebaseapp.com',
messagingSenderId: '0987654321',
},
production: false,
useEmulators: true
};
My Cloud Function runs great in the cloud but doesn't run in the emulators.
Each time I make a change in a Cloud Function, deploy the update to the cloud, wait a minute for the deploy to propagate, test my function, and wait for the logs to show up in the Firebase Console is ten minutes. I'm looking forward to using the emulators to speed up this development cycle.
Here's the rest of my code. I doubt there's anything wrong with these files.
The Cloud Function triggers from writing a message to Firestore, changes the message to uppercase, and writes the uppercase message to a new field in the document.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.uppercaseMe = functions.firestore.document('Triggers/{docId}').onCreate((snap, context) => {
var original = snap.data().message;
functions.logger.log('Uppercasing', context.params.docId, original);
var uppercase = original.toUpperCase();
return snap.ref.set({ uppercase }, { merge: true });
});
The HTML view has a form for submitting a message. It displays the data that was written to Firestore and then displays the results from the Cloud Function.
<form (ngSubmit)="triggerMe()">
<input type="text" [(ngModel)]="message" name="message" placeholder="Message" required>
<button type="submit" value="Submit">Submit</button>
</form>
<div>{{ data$ }}</div>
<div>{{ upperca$e }}</div>
The app.component.ts controller writes the message to Firestore, reads back the message from Firestore, then sets up a document listener to wait for the cloud function to write a new field to the document.
import { Component } from '#angular/core';
import { Firestore, doc, getDoc, collection, addDoc, onSnapshot } from '#angular/fire/firestore';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data$: any;
docSnap: any;
message: string | null = null;
upperca$e: string | null = null;
unsubMessage$: any;
constructor(public firestore: Firestore) {}
async triggerMe() {
try {
// write to Firestore
const docRef = await addDoc(collection(this.firestore, 'Triggers'), {
message: this.message,
});
this.message = null; // clear form fields
// read from Firestore
this.docSnap = await getDoc(doc(this.firestore, 'Triggers', docRef.id));
this.data$ = this.docSnap.data().message;
// document listener
this.unsubMessage$ = onSnapshot(doc(this.firestore, 'Triggers', docRef.id), (snapshot: any) => {
this.upperca$e = snapshot.data().uppercase;
});
} catch (error) {
console.error(error);
}
}
}
Firebase emulators work independently of Angular or other apps! I reread the documentation and learned that you just spin up the emulators,
firebase emulators:start
open your browser to http://localhost:4000, and you can write data in Firestore and then see the results of your function appear in Firestore. You can also read the logs. This only works with triggerable functions, not with callable functions.
Amazing what you can learn by reading the documentation. :-)

MSTeams Config page Angular 12 SPA with routing

I'm using Angular 12 and am writing a simple group tab app. I'm working on the config page component and the html looks like this:
<br />
<br />
<br />
<p>Configuration 3</p>
<input type="text" placeholder="Some Test" />
In a normal browser, the text and box appears. But if I try to do the same thing via the install to tab path, I don't get the text or input box at all.
I think this might have something to do with routing but can't confirm.
The app-routing-module is pretty simple:
const routes: Routes = [
{
path: '',
component: HomeComponent,
},
{
path: 'configuration',
component: ConfigurationComponent,
},
];
#NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation:
!BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
? 'enabled'
: 'disabled',
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}
So what does it take to get the SPA to route to the configuration page when used within teams?
Configuration Component: (URL purposed changed to protect the innocent)
import { Component, OnInit } from '#angular/core';
import { Inject, AfterViewInit, ElementRef } from '#angular/core';
import { DOCUMENT } from '#angular/common';
import * as microsoftTeams from '#microsoft/teams-js';
#Component({
selector: 'app-configuration',
templateUrl: './configuration.component.html',
styleUrls: ['./configuration.component.scss'],
})
export class ConfigurationComponent implements OnInit, AfterViewInit {
constructor(
#Inject(DOCUMENT) private document: Document,
private elementRef: ElementRef
) {}
ngOnInit(): void {
microsoftTeams.initialize();
}
ngAfterViewInit() {
console.log('Initializing ms teams');
microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
microsoftTeams.settings.setSettings({
entityId: '',
contentUrl: 'https://test.ngrok.io',
suggestedDisplayName: 'Test',
websiteUrl: 'https://test.ngrok.io',
});
saveEvent.notifySuccess();
});
console.log('Register on save');
microsoftTeams.settings.setValidityState(true);
}
}
Thanks,
Nick
In order to render a tab in Teams. You need to make sure that it is iFramable. Please see the document- Tab requirements.
Make sure that you have given the domain in valid domains in your manifest.
Please share more details like console error, what are you using static tab or config and manifest, if issue isn't solve for you
I traced my problem for this particular question to the line:
initialNavigation:
!BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
? 'enabled'
: 'disabled',
This example is in a lot of the code for SPAs in and Teams Tabs.
I just have it set to 'enabled' for now and I can get beyond the purpose of this question.

Custom validator in angular reactive forms is never firing

I have created a very simple custom validator for a simple form control, e.g. MatInput, which would always return non-null e.g. invalid. I hav also added one of the pre-built validators e.g. required. When I start my app I can see that status = INVALID and errors.required = true.
Once I start typing, I expected that status will remain INVALID and errors.myError = true, but this does not happen. What am I doing wrong? I have built my example on StackBlitz. I have also added the contents on my TS & HTML files below
TS
import { Component } from '#angular/core';
import { AbstractControl, FormControl, ValidationErrors, ValidatorFn, Validators} from '#angular/forms';
export function myValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
return { "myError": true };
};
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = new FormControl('', [Validators.required, myValidator]);
}
HTML
<label>
Name:
<input type="text" [formControl]="name">
</label>
{{ name | json }}
I am quite new to Angular and I am not sure how to debug this. What can I try next?
TL;DR:
export class AppComponent {
name = new FormControl('', [Validators.required, myValidator()]);
}
Explanation:
myValidator is not being called, so you are not getting the ValidatorFn

Vue Native: 'Invariant Violation'

I'm trying to get up and rolling with Vue Native, and I'm running into the same error whenever I attempt to navigate beyond the initial screen
Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite
components) but got: undefined. You likely forgot to export your
component from the file it's defined in, or you might have mixed up
default and named imports.
Check the render method of ReactVueComponent.
in ReactVueComponent (at SceneView.js:17)
in SceneView (at CardStack.js:466)
in RCTView (at View.js:60)
in View (at createAnimatedComponent.js:154)
in AnimatedComponent (at Card.js:12)
When searching my dir for ReactVueComponent, it doesn't exist, nor does SceneView.js, nor does RCTCView, etc. My guess is that's because they are generated with the code compiles?
My router, index.vue is set up as follows
<template>
<root>
<app-navigation></app-navigation>
</root>
</template>
<script>
import React from "react";
import { StackNavigator, navigationService } from "vue-native-router";
import { Root } from "native-base";
import WelcomeScreen from "./screen/WelcomeScreen.vue";
import HomeScreen from "./screen/home.vue";
const AppNavigation = StackNavigator(
{
Welcome: { screen: WelcomeScreen },
Home: { screen: HomeScreen }
},
{
initialRouteName: "Welcome",
headerMode: "none"
}
);
export default {
components: { Root, AppNavigation }
};
</script>
My WelcomeScreen component(this loads correctly. The button, on push, throws the error)
<template>
<nb-content padder>
<nb-form>
<view :style="{marginTop:300}">
<nb-button block :on-press="login">
<nb-text>Login</nb-text>
</nb-button>
</view>
</nb-content>
</template>
<script>
import { Dimensions, Platform, AsyncStorage } from "react-native";
import { NavigationActions } from "vue-native-router";
export default {
props: {
navigation: {
type: Object
}
},
methods: {
login() {
this.navigation.navigate("Home");
}
}
};
</script>
The HomeScreen component, which fails to render:
<template>
<nb-container :style="{flex:1, backgroundColor: '#fff'}">
<nb-header>
<nb-body>
<nb-title>title</nb-title>
</nb-body>
</nb-header>
<nb-content>
<nb-list>
<li>thing 1</li>
<li>thing 2</li>
<li>thing 3</li>
</nb-list>
</nb-content>
</nb-container>
</template>
<script>
import React from "react";
import { Dimensions } from "react-native";
const SCREEN_WIDTH = Dimensions.get("window").width;
export default {
props: {
navigation: Object
}
};
</script>
Any tips on this would be much appreciated. Not much out there on Vue Native yet, and I've tried to follow the few examples I've seen to the best of my ability. Double and triple-checked my dependencies and they all seem to be in place.
Seems like you are using <li> tags which are not supported. If you check native base docs. The correct tag to be used within nb-list is nb-list-item. http://docs.nativebase.io/Components.html#list-def-headref

Load external image source using variable

I couldn't load an external image using an url variable.
The problem is that Angular auto complete the url to add localhost:3000/ at the beguining of the url.
Here is my component:
import { Component, Input, OnInit } from '#angular/core';
import { Cite } from './cite';
import { Engin } from './engin';
import { Station } from './station';
import { EnginService } from './engin.service';
#Component({
moduleId: module.id,
selector: 'my-engin-detail',
template: `
{{imgUrl}}
<img [src]= "imgUrl" width="42" height="42" />
`,
styles: [`
`]
})
export class EnginDetailComponent implements OnInit {
constructor(private enginService: EnginService) {
}
#Input()
engin: Engin;
imgUrl: string;
ngOnInit(): void {
this.enginService.getImgUrl(this.engin.id_engin)
.then(url => this.imgUrl = url);
}
}
the output is
192.168.0.102/resultsdetails/image/assets/E207_1.png //that's => {{imgUrl}}
ERROR 404 :
http://localhost:3000/192.168.0.102/resultsdetails/image/assets/E207_1.png
404 (Not Found)
Here angular2 compiler autocomplete the url with "http://localhost:3000/" and i don't want that.
However this works fine:
<img src="192.168.0.102/resultsdetails/image/assets/E207_1.png" width="42" height="42/>
So, I don't know how to inject a variable in the [src] without an autocompletion with /localhost:3000
In case of "relative" url you have, it could be done like this:
<img [src]= "'//' + imgUrl" width="42" height="42" />

Resources