I am new to angular. I have created an webapplication using vs2015 and latest angular packages.
When i try to call my service from component on button click event then i am getting below error in browser console window.
Error: -
ERROR Error: Uncaught (in promise): Error: StaticInjectorError[ViewCountService]:
StaticInjectorError[ViewCountService]:
NullInjectorError: No provider for ViewCountService!
Error: StaticInjectorError[ViewCountService]:
StaticInjectorError[ViewCountService]:
NullInjectorError: No provider for ViewCountService!
at _NullInjector.get (injector.js:31)
at resolveToken (injector.js:387)
at tryResolveToken (injector.js:330)
at StaticInjector.get (injector.js:170)
at resolveToken (injector.js:387)
at tryResolveToken (injector.js:330)
at StaticInjector.get (injector.js:170)
at resolveNgModuleDep (ng_module.js:103)
at NgModuleRef_.get (refs.js:1037)
at resolveDep (provider.js:455)
at _NullInjector.get (injector.js:31)
at resolveToken (injector.js:387)
at tryResolveToken (injector.js:330)
at StaticInjector.get (injector.js:170)
at resolveToken (injector.js:387)
at tryResolveToken (injector.js:330)
at StaticInjector.get (injector.js:170)
at resolveNgModuleDep (ng_module.js:103)
at NgModuleRef_.get (refs.js:1037)
at resolveDep (provider.js:455)
at resolvePromise (zone.js:824)
at resolvePromise (zone.js:795)
at zone.js:873
at ZoneDelegate.invokeTask (zone.js:425)
at Object.onInvokeTask (ng_zone.js:575)
at ZoneDelegate.invokeTask (zone.js:424)
at Zone.runTask (zone.js:192)
at drainMicroTaskQueue (zone.js:602)
at ZoneTask.invokeTask [as invoke] (zone.js:503)
at invokeTask (zone.js:1540)
defaultErrorLogger # errors.js:48
ErrorHandler.handleError # error_handler.js:90
next # application_ref.js:311
schedulerFn # event_emitter.js:156
SafeSubscriber.__tryOrUnsub # Subscriber.ts:254
SafeSubscriber.next # Subscriber.ts:204
Subscriber._next # Subscriber.ts:135
Subscriber.next # Subscriber.ts:95
Subject.next # Subject.ts:61
EventEmitter.emit # event_emitter.js:131
(anonymous) # ng_zone.js:605
ZoneDelegate.invoke # zone.js:392
Zone.run # zone.js:142
NgZone.runOutsideAngular # ng_zone.js:404
onHandleError # ng_zone.js:605
ZoneDelegate.handleError # zone.js:396
Zone.runGuarded # zone.js:158
_loop_1 # zone.js:702
api.microtaskDrainDone # zone.js:711
drainMicroTaskQueue # zone.js:610
ZoneTask.invokeTask # zone.js:503
invokeTask # zone.js:1540
globalZoneAwareCallback # zone.js:1566
my app.module.ts: -
import { NgModule, Injectable } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { RouterModule, Routes } from '#angular/router';
import { HttpModule } from '#angular/http';
import { ModalModule } from 'ngx-modialog';
import { BootstrapModalModule, Modal, bootstrap4Mode } from 'ngx-modialog/plugins/bootstrap';
import { AppComponent } from './app.component';
import { ExportToPdfComponent } from './exportTopdf/exportTopdf.component';
import { InvalidPageComponent } from './invalidPage/invalidPage.component';
import { ViewCountService } from './Service/viewsCount.component';
const appRoutes: Routes = [
{ path: 'home', component: AppComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'export', component: ExportToPdfComponent },
{ path: '**', component: InvalidPageComponent }
];
#NgModule({
imports: [
BrowserModule,
HttpModule,
RouterModule.forRoot(appRoutes),
ModalModule.forRoot(),
BootstrapModalModule
],
declarations: [
AppComponent,
ExportToPdfComponent,
InvalidPageComponent
],
bootstrap: [
AppComponent
],
providers: [
ViewCountService
]
})
export class AppModule { }
exportTopdf.component.ts file: -
import { Component, ViewContainerRef, ViewEncapsulation } from '#angular/core';
import { ViewCountService } from '../Service/ViewsCount.component';
import { Overlay } from 'ngx-modialog';
import { Modal } from 'ngx-modialog/plugins/bootstrap';
#Component({
selector: 'export-to-pdf',
templateUrl: 'app/exportTopdf/exportTopdf.component.html',
})
export class ExportToPdfComponent {
name: string;
fields: any;
constructor(public modal: Modal, private ViewCountService: ViewCountService) {
debugger;
this.fields = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
}
getViewCount(): void {
debugger;
this.ViewCountService.getViewCount()
.then(data => {
this.name = data;
console.log("I CANT SEE DATA HERE: ", this.name)
});
}
}
my service code: -
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ViewCountService {
constructor(private http: Http) {
}
getViewCount() {
return this.http.get('api/Tableau/GetViewsCount')
.map(response => response.json() as string).toPromise();
}
getDataObservable(url: string) {
return this.http.get('api/Tableau/GetViewsCount')
.map(data => {
data.json();
console.log("I CAN SEE DATA HERE: ", data.json());
});
}
}
system.config.js: -
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
paths: {
// paths serve as alias
'npm:': '/node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'#angular/core': 'npm:#angular/core/bundles/core.umd.js',
'#angular/common': 'npm:#angular/common/bundles/common.umd.js',
'#angular/compiler': 'npm:#angular/compiler/bundles/compiler.umd.js',
'#angular/platform-browser': 'npm:#angular/platform-browser/bundles/platform-browser.umd.js',
'#angular/platform-browser-dynamic': 'npm:#angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'#angular/http': 'npm:#angular/http/bundles/http.umd.js',
'#angular/router': 'npm:#angular/router/bundles/router.umd.js',
'#angular/forms': 'npm:#angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
'ngx-modialog': 'npm:ngx-modialog/bundle/ngx-modialog.umd.min.js',
'ngx-modialog/plugins/bootstrap': 'npm:ngx-modialog/plugins/bootstrap/bundle/ngx-modialog-bootstrap.umd.min.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
package.json: -
{
"name": "angular-quickstart",
"version": "1.0.0",
"description": "QuickStart package.json from the documentation, supplemented with testing support",
"scripts": {
"build": "tsc -p src/",
"build:watch": "tsc -p src/ -w",
"build:e2e": "tsc -p e2e/",
"serve": "lite-server -c=bs-config.json",
"serve:e2e": "lite-server -c=bs-config.e2e.json",
"prestart": "npm run build",
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
"pree2e": "npm run build:e2e",
"e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first",
"preprotractor": "webdriver-manager update",
"protractor": "protractor protractor.config.js",
"pretest": "npm run build",
"test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
"pretest:once": "npm run build",
"test:once": "karma start karma.conf.js --single-run",
"lint": "tslint ./src/**/*.ts -t verbose"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"#angular/common": "~5.0.3",
"#angular/compiler": "~5.0.3",
"#angular/core": "~5.0.3",
"#angular/forms": "~5.0.3",
"#angular/http": "~5.0.3",
"#angular/platform-browser": "~5.0.3",
"#angular/platform-browser-dynamic": "~5.0.3",
"#angular/router": "~5.0.3",
"angular-in-memory-web-api": "~0.5.1",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"ngx-modialog": "^5.0.0",
"rxjs": "5.5.2",
"systemjs": "0.20.19",
"zone.js": "^0.8.4"
},
"devDependencies": {
"concurrently": "^3.2.0",
"lite-server": "^2.2.2",
"typescript": "~2.6.1",
"canonical-path": "0.0.2",
"tslint": "^5.8.0",
"lodash": "^4.16.4",
"jasmine-core": "~2.8.0",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.2.0",
"rimraf": "^2.5.4",
"#types/node": "^8.0.53",
"#types/jasmine": "2.8.2"
},
"repository": {}
}
Thanks in advance for your valuable feedbacks and comments.
Look at your impors
app.module.ts
import { ViewCountService } from './Service/viewsCount.component';
exportTopdf.component.ts
import { ViewCountService } from '../Service/ViewsCount.component';
Since Systemjs is case-sensitive(See Service instance is not available for child component in angular2)
looks like you're importing different modules
ViewsCount
|
viewsCount
So choose only one option to import your service
app.module.ts
import { ViewCountService } from './Service/viewsCount.component';
exportTopdf.component.ts
import { ViewCountService } from '../Service/viewsCount.component';
^
instead of V
Read also style guide to better understanding how to name your files
If you are using Angular 4.3+ until 5 you will need to import HttpClient in your app.module from the new library
> import { HttpClientModule } from '#angular/common/http';
imports: [
YModule,
XModule,
HttpClientModule
],
In your service :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ViewCountService {
constructor(private http: HttpClient ) {
}
getViewCount() {
return this.http.get('api/Tableau/GetViewsCount');
}
...
}
In your component:
getViewCount(): void {
this.ViewCountService.getViewCount()
.subscribe(
data =>{console.log(data);//your data},
err=>{console.log(err);},
() => {console.log("Done loading");}
);
}
I was facing the same issue then I have added below line in app.module and it worked for me.
import { HttpClientModule } from ‘#angular/common/http’;
And add it to the
#NgModule({ imports: [ ..., HttpClientModule,... ]
It doesn't look to be solved yet:
https://github.com/angular/angular/issues/20339
As a work around, try to make all your constructor() functions without parameters by extracting the as properties.
ExportToPdfComponent:
import { Component, ViewContainerRef, ViewEncapsulation } from '#angular/core';
import { ViewCountService } from '../Service/ViewsCount.component';
import { Overlay } from 'ngx-modialog';
import { Modal } from 'ngx-modialog/plugins/bootstrap';
#Component({
selector: 'export-to-pdf',
templateUrl: 'app/exportTopdf/exportTopdf.component.html',
})
export class ExportToPdfComponent {
name: string;
fields: any;
modal: Modal;
ViewCountService: ViewCountService;
constructor() {
debugger;
this.fields = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
}
getViewCount(): void {
debugger;
this.ViewCountService.getViewCount()
.then(data => {
this.name = data;
console.log("I CANT SEE DATA HERE: ", this.name)
});
}
}
ViewCountService:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ViewCountService {
private http: Http;
constructor() {
}
getViewCount() {
return this.http.get('api/Tableau/GetViewsCount')
.map(response => response.json() as string).toPromise();
}
getDataObservable(url: string) {
return this.http.get('api/Tableau/GetViewsCount')
.map(data => {
data.json();
console.log("I CAN SEE DATA HERE: ", data.json());
});
}
}
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
routing
],
add this into your app.module.ts file
Related
I'm having some issues with installing vue-router with laravel. Laravel 9, Vue.js 3
routes.js
import AllBooks from './components/AllBooks.vue';
import CreateBook from './components/CreateBook.vue';
import EditBook from './components/EditBook.vue';
export const routes = [
{
name: 'home',
path: '/',
component: AllBooks
},
{
name: 'create',
path: '/create',
component: CreateBook
},
{
name: 'edit',
path: '/edit/:id',
component: EditBook
}
];
Here's my config:-
resources/js/app.js
import './bootstrap';
import { createApp } from 'vue';
const app = createApp({})
import App from './App.vue';
import VueAxios from 'vue-axios';
import VueRouter from 'vue-router'; // problem!!
import axios from 'axios';
import { routes } from './routes';
const router = VueRouter.createRouter({
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
history: VueRouter.createWebHashHistory(),
routes,
})
// Make sure to _use_ the router instance to make the
// whole app router-aware.
app.use(router)
app.mount('#app')
package.json
"devDependencies": {
"#popperjs/core": "^2.11.6",
"#vitejs/plugin-vue": "^3.0.1",
"axios": "^1.1.2",
"bootstrap": "^5.2.3",
"laravel-vite-plugin": "^0.7.2",
"lodash": "^4.17.19",
"postcss": "^8.1.14",
"sass": "^1.56.1",
"vite": "^4.0.0",
"vue": "^3.2.37"
},
"dependencies": {
"vue-axios": "^3.5.2",
"vue-router": "^4.1.6"
}
If you could help me with vue-router doesn't provide an export?
import VueRouter from '/node_modules/.vite/deps/vue-router.js?v=05a616f1';
In my case, look out for duplicated entries for our vue libraries. I had:-
resources/views/layouts/app.blade.php
<script src="https://unpkg.com/vue#3"></script>
<script src="https://unpkg.com/vue-router#4"></script>
AND
package.json
"dependencies": {
"vue": "^3.2.45",
"vue-router": "^4.1.6"
}
also, my config changed in:-
resources/js/app.js
import './bootstrap';
import * as Vue from 'vue';
import * as VueRouter from 'vue-router'; // alternative ways of importing
import LoginComponent from "./components/LoginComponent.vue";
import TestComponent from "./components/TestComponent.vue";
const Home = { template: '<div>Home</div>' }
const routes = [
{ path: '/', component: Home },
{ path: '/login', name: "Login", component: LoginComponent },
{ path: '/test', name: "Test", component: TestComponent },
]
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
})
const app = Vue.createApp({})
app.use(router)
app.mount('#app')
If this helps you, well keep truckin. :)
I'm currently building an external component library using Vue 3 + Vite. I'm using 3rd party component and style, but the style doesn't apply when I used it in my main project. It used to work before when I use Vue 2 + Vue CLI.
My component library project looks like this:
and here's the detail for my code
vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, 'src/main.js'),
name: 'custom-lib',
fileName: 'custom-lib',
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
}
}
}
})
package.json
{
"name": "custom-lib",
"private": true,
"version": "0.0.0",
"type": "module",
"files": [
"dist"
],
"main": "./dist/custom-lib.umd.cjs",
"module": "./dist/custom-lib.js",
"exports": {
".": {
"import": "./dist/custom-lib.js",
"require": "./dist/custom-lib.umd.cjs"
}
},
"scripts": {
"build": "vite build"
},
"dependencies": {
"moment": "^2.29.4",
"vue": "^3.2.41",
"vue-datepicker-next": "^1.0.2"
},
"devDependencies": {
"#vitejs/plugin-vue": "^3.2.0",
"sass": "^1.56.0",
"sass-loader": "^13.1.0",
"vite": "^3.2.3"
}
}
src/components/Datepicker.vue
<template>
<DatePicker
:id="id"
v-model:value="inputVal"
value-type="date"
type="date"
:format="dateFormat"
:placeholder="dateFormat"
:disabled="disabled"
input-class="mx-input"
/>
</template>
<script>
import DatePicker from 'vue-datepicker-next';
import moment from 'moment';
export default {
name: 'Datepicker',
components: {
DatePicker
},
props: {
id: {
type: String,
required: true
},
modelValue: null,
dateFormat: String,
disabled: Boolean
},
computed: {
inputVal: {
get() {
if (this.modelValue) {
return moment(this.modelValue).toDate();
}
return null;
},
set(val) {
let strVal = undefined;
let m = moment(val);
if (m.isValid()) {
strVal = m.format("YYYY-MM-DDTHH:mm:ss");
}
this.$emit('update:modelValue', strVal);
}
}
}
};
</script>
<style lang="scss">
#import "vue-datepicker-next/scss/index.scss";
</style>
src/main.js
import Datepicker from './components/Datepicker.vue';
export {
Datepicker
}
My Datepicker style not working in my main project, is there something missing from the config?
As I was suggesting in comment, you can use vite-plugin-css-injected-by-js
Add the plugin to your component project:
npm i vite-plugin-css-injected-by-js --save
Add the plugin to vite config of your custom component:
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js' // 👈
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
cssInjectedByJsPlugin() // 👈
],
build: {
lib: {
entry: resolve(__dirname, 'src/main.js'),
name: 'custom-lib',
fileName: 'custom-lib',
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
}
}
}
})
It should work without hassle.
See it on Stackblitz:
you can go in test folder (cd test) and run yarn dev to launch the preview.
preview:
https://stackblitz.com/edit/vitejs-vite-yd1rzw
Result:
I am trying build a vue 3 components library. Everything works fine. However I realized that the vueuse composables do not work when I try to use the library. This my first time writing a library. Is there any wrong with my code? Is there anything extra I need to do?. this my code, package.json and vite.config.ts. Any help will be much appreciated.
// code
setup(props, { slots }) {
const isLargeScreen = useMediaQuery("(min-width: 1024px)");
watch(
isLargeScreen,
(val) => {
console.log(val) //does not work;
}
);
return ()=><div>Hello world</div>
}
//package.json
{
"name": "my-lib",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"#popperjs/core": "^2.11.5",
"#vitejs/plugin-vue-jsx": "^2.0.0",
"#vueuse/core": "^8.9.4",
"vue": "^3.2.37"
},
"devDependencies": {
"#vitejs/plugin-vue": "^3.0.0",
"typescript": "^4.6.4",
"vite": "^3.0.0",
"vite-plugin-dts": "^1.3.0",
"vite-plugin-windicss": "^1.8.7",
"vue-tsc": "^0.38.4",
"windicss": "^3.5.6"
},
"files": [
"dist"
],
"module": "./dist/my-lib.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/my-lib.mjs"
},
"./dist/style.css": "./dist/style.css"
}
}
// vite.config.ts
import { defineConfig } from "vite";
import { fileURLToPath, URL } from "url";
import { resolve } from "path";
import vue from "#vitejs/plugin-vue";
import dts from "vite-plugin-dts";
import WindiCSS from "vite-plugin-windicss";
import vueJsx from "#vitejs/plugin-vue-jsx";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
WindiCSS(),
vueJsx({}),
dts({
insertTypesEntry: true,
}),
],
esbuild: {
jsxFactory: "h",
jsxFragment: "Fragment",
},
resolve: {
alias: {
"#": fileURLToPath(new URL("./src", import.meta.url)),
},
},
build: {
lib: {
entry: resolve(__dirname, "src/main.ts"),
name: "MyLib",
fileName: "my-lib",
},
rollupOptions: {
external: ["vue", "#vueuse/core"],
output: {
globals: {
vue: "Vue",
"#vueuse/core": "VueCore",
},
},
},
},
});
Okay I just started using angular firebase and I've been scratching my head for two days. Most of the tutorials out there are mostly for older versions of firebase
this is the error that I am receiving when I inject my authentication service to the component
Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[LoginService -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
NullInjectorError: R3InjectorError(AppModule)[LoginService -> AngularFireAuth -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options -> InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
the following links haven't been helpful so far
Link1 Link2 Link3
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { HttpClientModule } from '#angular/common/http';
import { LoginService } from 'src/services/login.service';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './components/login/login.component';
import { HomeComponent } from './components/home/home.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { environment } from '../environments/environment';
import { initializeApp,provideFirebaseApp } from '#angular/fire/app';
import { provideAuth,getAuth } from '#angular/fire/auth';
import { provideDatabase,getDatabase } from '#angular/fire/database';
import { provideFirestore,getFirestore } from '#angular/fire/firestore';
import { AngularFirestore } from '#angular/fire/compat/firestore';
#NgModule({
declarations: [
AppComponent,
LoginComponent,
HomeComponent,
NotFoundComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
provideDatabase(() => getDatabase()),
provideFirestore(() => getFirestore()),
FormsModule,
ReactiveFormsModule
],
providers: [LoginService],
bootstrap: [AppComponent]
})
export class AppModule { }
login.service.ts
import { Injectable } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/compat/auth';
import { Router } from '#angular/router';
import { AngularFirestore } from '#angular/fire/compat/firestore';
#Injectable({
providedIn: 'root'
})
export class LoginService {
userLoggedIn: boolean;
constructor(private afAuth: AngularFireAuth, private router : Router, private afs: AngularFirestore) {
this.userLoggedIn = false;
}
loginUser(email: string, password: string): Promise<any> {
return this.afAuth.signInWithEmailAndPassword(email,password)
.then(() => {
console.log('Auth Service: loginUser: success');
this.router.navigate(['']);
})
.catch(error => {
console.log('Auth Service: login error...');
console.log('error code', error.code);
console.log('error', error);
if (error.code)
return { isValid: false, message: error.message };
else
return { isValid: false, message : "Login Error"}
});
}
}
login.component.ts
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { LoginService } from 'src/services/login.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
loginFormCtrl: FormGroup;
constructor(private LoginService: LoginService, private router: Router) {
this.loginFormCtrl = new FormGroup({
email: new FormControl('', Validators.required),
password: new FormControl(null, Validators.required)
})
}
ngOnInit(): void {
}
onLogin() {
if (this.loginFormCtrl.invalid)
return;
this.LoginService.loginUser(this.loginFormCtrl.value.email, this.loginFormCtrl.value.password).then((result) => {
if (result == null) {
console.log('logging in...');
this.router.navigate(['']);
}
else if (result.isValid == false) {
console.log('login error', result);
}
});
}
}
package.json
{
"name": "fire-base",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"#angular/animations": "~12.2.0",
"#angular/common": "~12.2.0",
"#angular/compiler": "~12.2.0",
"#angular/core": "~12.2.0",
"#angular/fire": "^7.1.1",
"#angular/forms": "~12.2.0",
"#angular/platform-browser": "~12.2.0",
"#angular/platform-browser-dynamic": "~12.2.0",
"#angular/router": "~12.2.0",
"rxjs": "~6.6.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4",
"firebase": "^9.1.0",
"rxfire": "^6.0.0"
},
"devDependencies": {
"#angular-devkit/build-angular": "~12.2.7",
"#angular/cli": "~12.2.7",
"#angular/compiler-cli": "~12.2.0",
"#types/jasmine": "~3.8.0",
"#types/node": "^12.11.1",
"jasmine-core": "~3.8.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.3.5"
}
}
The fix is actually really simple...
in your app.module.ts
import
import { FIREBASE_OPTIONS } from '#angular/fire/compat';
and then add this to the providers
providers: [
{ provide: FIREBASE_OPTIONS, useValue: environment.firebase }
],
It seems that AngularFire is in the middle of major changes that embrace Firebase's new modular API, but the documentation hasn't quite caught up. The gist is that the you are initializing the Firebase App using the new API, but trying to use Firebase resources with the old API.
The key is to look at the import statement. Compare old and new style of initializing the app:
import {AngularFireModule} from '#angular/fire/compat';
[...]
imports: [
AngularFireModule.initializeApp(environment.firebase),
]
vs.
import {initializeApp, provideFirebaseApp} from '#angular/fire/app';
[...]
imports: [
provideFirebaseApp( () => initializeApp(environment.firebase)),
]
Notice how the old style initialization is under the "compat" namespace. If you initialize your app this way, you must also use the compat libraries to access resources. For example, from the AngularFire docs:
import {AngularFirestore, AngularFirestoreDocument} from '#angular/fire/compat/firestore';
[...]
constructor(private afs: AngularFirestore) {
this.itemDoc = afs.doc<Item>('items/1');
this.item = this.itemDoc.valueChanges();
}
However, this doesn't work with the new style app initialization. Instead you must use something like this, adapted from the Firebase docs:
import {doc, Firestore, getDoc} from '#angular/fire/firestore';
[...]
constructor(private firestore: Firestore) { }
[...]
const docRef = doc(this.firestore, "cities", "SF");
const docSnap = await getDoc(docRef);
Why does it matter? I assume, but do not know, that the instance of the app is not shared between old and new.
So, the moral of the story:
Most of the documentation on the internet uses the old API. If you want to use that API, use the "compat" version of the library.
If you want to use the modern modular API, make sure you're not using the "compat" library. And the best bet is to refer to the Firebase documentation until the AngularFire team has had a chance to catch up.
I had the same problem today and I agree: there's many versions and their documentation is disappointing.
The solution (in my case)
For my setup (angular 11 + angular/fire 6.1.5) i had to put the following in my app.module.ts file:
...
imports: [
...
AngularFireModule.initializeApp(environment.firebase),
],
...
(For me environment.firebase contains my firebase config.)
Further analysis of the problem below, you can stop reading if you don't care
The documentation for angular/fire 7 will tell you to do this:
provideFirebaseApp(() => initializeApp(environment.firebase)),
Which I'm sure works great for version 7, but angular 11 automatically installs version 6, because that's compatible.
My issue was fixed when I used both versions of initialization
AngularFireModule.initializeApp(environment.firebase),
provideFirebaseApp(() => initializeApp(environment.firebase)),
I have the same issue using auth-guard.
Fixed it by dropping the compat in the import path.
Before
import { hasCustomClaim, canActivate } from '#angular/fire/compat/auth-guard';
After
import { hasCustomClaim, canActivate } from '#angular/fire/auth-guard';
On the file App.component.ts add the following provider object:
import { FIREBASE_OPTIONS } from '#angular/fire/compat';
#NgModule({
providers: [
{ provide: FIREBASE_OPTIONS, useValue: environment.firebaseConfig }
],
})
taked from: [https://www.angularfix.com/2022/03/angular-fire-no-provider-for.html][1]
As advised by #Daniel Eisenhardt I downgraded my angular version. And it is now working ! Angular : ~11.2.4
and running ng add #angular/fire installed a compatible version of angular fire. ("^6.1.5")
This is my updated file
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './component/home/home.component';
import { LoginComponent } from './component/login/login.component';
import { NotFoundComponent } from './component/not-found/not-found.component';
import { AngularFireModule } from '#angular/fire';
import { AngularFirestoreModule } from '#angular/fire/firestore';
import { AngularFireDatabaseModule } from '#angular/fire/database';
// import { AngularFireStorageModule } from '#angular/fire/storage';
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
LoginComponent,
NotFoundComponent
],
imports: [
BrowserModule,
AppRoutingModule,
AngularFireModule.initializeApp(environment.firebase)
AngularFirestoreModule,
AngularFireDatabaseModule,
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
package.json
{
"name": "evnt-mgmnt-app",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"#angular/animations": "~11.2.14",
"#angular/common": "~11.2.14",
"#angular/compiler": "~11.2.14",
"#angular/core": "~11.2.14",
"#angular/fire": "^6.1.5",
"#angular/forms": "~11.2.14",
"#angular/platform-browser": "~11.2.14",
"#angular/platform-browser-dynamic": "~11.2.14",
"#angular/router": "~11.2.14",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3",
"firebase": "^7.0 || ^8.0"
},
"devDependencies": {
"#angular-devkit/build-angular": "~0.1102.13",
"#angular/cli": "~11.2.15",
"#angular/compiler-cli": "~11.2.14",
"#types/jasmine": "~3.6.0",
"#types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.1.5",
"#angular-devkit/architect": ">= 0.900 < 0.1300",
"firebase-tools": "^8.0.0 || ^9.0.0",
"fuzzy": "^0.1.3",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
"open": "^7.0.3",
"jsonc-parser": "^3.0.0"
}
}
This error is likely a sign that you've not fully migrated to v9 Firebase.
v9 Firebase
Examples of v9 Firebase code
https://dev.to/jdgamble555/angular-12-with-firebase-9-49a0
compat imports as an intermediate step
A simple first-step migration will require changing imports to compat versions.
This keeps code working but you'll not benefit from v9 tree-shaking.
The compat imports gives APIs compatible with v8 code BUT the provide methods e.g. provideFirebaseApp won't work until all imports are v9 imports i.e. back to not being compat imports.
So until ALL your relevant code is upgraded to v9 use the old way
import { AngularFireAuthModule } from '#angular/fire/compat/auth';
import { AngularFireModule } from '#angular/fire/compat';
import { AngularFirestoreModule } from '#angular/fire/compat/firestore';
import { AngularFireAuthGuardModule } from '#angular/fire/compat/auth-guard';
#NgModule({
declarations: [
...
],
imports: [
...
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule,
AngularFireAuthModule,
AngularFireAuthGuardModule,
],
...
})
export class AppModule { }
Once your code is fully migrated go back to the provide methods:
import { initializeApp, provideFirebaseApp } from '#angular/fire/app';
import { provideAuth, getAuth } from '#angular/fire/auth';
import { provideFirestore, getFirestore } from '#angular/fire/firestore';
#NgModule({
declarations: [
...
],
imports: [
...
// firebase
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
provideFirestore(() => getFirestore()),
I am working on a app with angular 8 and ngrx . Everything was smooth until I tested my app in Incognito mode . Suddenly a url started to work very slow almost 50 seconds between navigation start and navigation end . I observed enabling redux-devtool extension seems to fix the issue(worked everytime) . Not sure whats happening here . The resolver for the route thats taking time has 3 api calls to make but these calls are happening the moment i click for the route yet resolver takes 50 sec to come to the component. Here are the details. Do let me know if anything else is required :
package.json
{
"name": "sfe",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --configuration=proxy --proxy-config proxy.config.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"#angular/animations": "~8.2.14",
"#angular/common": "~8.2.14",
"#angular/compiler": "~8.2.14",
"#angular/core": "~8.2.14",
"#angular/forms": "~8.2.14",
"#angular/platform-browser": "~8.2.14",
"#angular/platform-browser-dynamic": "~8.2.14",
"#angular/router": "~8.2.14",
"#auth0/angular-jwt": "^4.0.0",
"#ngrx/effects": "^8.6.0",
"#ngrx/entity": "^8.6.0",
"#ngrx/router-store": "^8.6.0",
"#ngrx/schematics": "^8.6.0",
"#ngrx/store": "^8.6.0",
"#ngrx/store-devtools": "^8.6.0",
"#ngx-translate/core": "^12.1.2",
"#ngx-translate/http-loader": "^4.0.0",
"core-js": "^2.5.4",
"include-media": "^1.4.9",
"moment": "^2.24.0",
"moment-timezone": "^0.5.28",
"node-sass": "~4.13.0",
"rxjs": "~6.4.0",
"rxjs-compat": "~6.4.0",
"tslib": "~1.9.0",
"webcomponents.js": "^0.7.24",
"zone.js": "~0.9.1"
},
"devDependencies": {
"#angular-devkit/build-angular": "~0.803.25",
"#angular-devkit/build-webpack": "^0.803.2",
"#angular/cli": "~8.3.25",
"#angular/compiler-cli": "~8.2.14",
"#angular/language-service": "~8.2.14",
"#types/node": "~8.9.4",
"#types/jasmine": "~3.3.8",
"#types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
}
app.module
import { AppEffects } from './app.effects';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA, ErrorHandler } from '#angular/core';
import { TranslateModule, TranslateService, TranslateLoader } from '#ngx-translate/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '#ngrx/store';
import { reducers, metaReducers } from './reducers';
import { CoreModule } from './core/core.module';
import { AuthModule } from './features/auth/auth.module';
import { StoreDevtoolsModule } from '#ngrx/store-devtools';
import { environment } from '../environments/environment';
import { EffectsModule, Actions } from '#ngrx/effects';
import {RouterState, StoreRouterConnectingModule } from '#ngrx/router-store';
import { HttpClient } from '#angular/common/http';
import { TranslateHttpLoader } from '#ngx-translate/http-loader';
import { GlobalErrorHandler } from './core/error-handling/global-exception-handling';
import { JwtHelperService, JWT_OPTIONS } from '#auth0/angular-jwt';
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
import { WindowRefService } from './core/services/window-ref.service';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AuthModule.forRoot(),
TranslateModule.forRoot(),
CoreModule.forRoot(),
AppRoutingModule,
StoreRouterConnectingModule.forRoot({
stateKey: 'router',
routerState:RouterState.Minimal
}),
StoreModule.forRoot(reducers, {
metaReducers,
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true
}
}),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),
EffectsModule.forRoot([AppEffects]),
StoreRouterConnectingModule.forRoot(),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [
WindowRefService,
{provide : LocationStrategy , useClass: HashLocationStrategy},
{
provide: TranslateService,
useClass: I18nService
},
{
provide: I18nService,
useExisting: TranslateService
},
{
provide: JWT_OPTIONS, useValue: JWT_OPTIONS
},
JwtHelperService,
{ provide: "BASE_API_URL", useValue: environment.apiUrl },
{ provide: ErrorHandler, useClass: GlobalErrorHandler }
],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
RoutingModule.ts(lazy loaded)
import { NotificationsResolver } from './resolvers/notifications.resolver';
import { NgModule } from '#angular/core';
import { Routes, RouterModule, CanActivate } from '#angular/router';
import { AuthGuard } from '../../auth/auth.guards';
const settingsroutes: Routes = [
{
path: '',
component: SettingsComponent,
children: [
{ path: 'notifications', component: SettingsComponent, data: { 'index': 1 }, resolve:{NotificationsResolver}},
],
canActivate: [AuthGuard]
}
];
#NgModule({
imports: [RouterModule.forChild(settingsroutes)],
exports: [RouterModule]
})
export class SettingsRoutingModule { }
Resolver.ts
import { loadContactInfo, loadNotificationMobileCarriers } from './../ngrx/settings.actions';
import { areNotificationSettingsLoaded, areContactInfoLoaded, areNotificationMobileCarriersLoaded } from './../ngrx/settings.selectors';
import { getUserDetails } from '../../../auth/ngrx/auth.selectors';
import { User } from 'src/app/core/models/User';
import { tap, filter, finalize, first } from 'rxjs/operators';
import { Observable, concat, merge } from 'rxjs';
import { Store, select } from '#ngrx/store';
import { AppState } from '../../../../reducers/index';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Injectable } from '#angular/core';
import { NotificationsRequestModel } from '../models/requestmodel/notifications-request.model';
import { loadNotificationSettings } from '../ngrx/settings.actions';
#Injectable()
export class NotificationsResolver implements Resolve<any>{
loadingNotifications = false;
loadingContactInfo = false;
loadingMobileCarriers = false;
notificationsRequest :NotificationsRequestModel = new NotificationsRequestModel();
userDetail$ : Observable<any>;
userDetail :User;
constructor(private store: Store<AppState>){
this.notificationsRequest.userId = 4000;
}
resolve(route:ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any>{
const notificationSettingsData$=
this.store.pipe(
select(areNotificationSettingsLoaded),
tap(notificationSettingsLoaded => {
if(!this.loadingNotifications && !notificationSettingsLoaded){
this.loadingNotifications = true;
this.store.dispatch(loadNotificationSettings({requestParameter :this.notificationsRequest}));
}
}),
filter(NotificationSettingsLoaded => NotificationSettingsLoaded),
first(),
finalize(()=> {
this.loadingNotifications = false;
})
);
const contactInfoData$ =this.store.pipe(
select(areContactInfoLoaded),
tap(areContactInfoLoaded => {
if(!this.loadingContactInfo && !areContactInfoLoaded){
this.loadingContactInfo = true;
this.store.dispatch(loadContactInfo({requestParameter :this.notificationsRequest}));
}
}),
filter(ContactInfoLoaded => ContactInfoLoaded),
first(),
finalize(()=> {
this.loadingContactInfo = false;
})
);
const noificationMobileCarriers$ =this.store.pipe(
select(areNotificationMobileCarriersLoaded),
tap(areNotificationMobileCarriersLoaded => {
if(!this.loadingMobileCarriers && !areNotificationMobileCarriersLoaded){
this.loadingMobileCarriers = true;
this.store.dispatch(loadNotificationMobileCarriers());
}
}),
filter(NotificationMobileCarriersLoaded=> NotificationMobileCarriersLoaded),
first(),
finalize(()=> {
this.loadingMobileCarriers = false;
console.log("third one")
})
);
//This is done to enable two calls being made simultaneoulsy
return merge(contactInfoData$, notificationSettingsData$, noificationMobileCarriers$)
}
}
appComponent.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
t1 : any;
t0 : any;
constructor(translate: TranslateService, private router: Router, private activatedRoute: ActivatedRoute,
private store: Store<AppState>) {
}
ngOnInit(): void {
this.router.events.subscribe(event => {
switch (true) {
case event instanceof NavigationStart: {
this.t0 = performance.now();
this.loading = true;
break;
}
case event instanceof NavigationEnd:
case event instanceof NavigationCancel:
case event instanceof NavigationError: {
this.t1 = performance.now();
this.loading = false;
console.log(this.t1-this.t0);
break;
}
default: {
break;
}
}
});
//load sub params
}
}
The issue I see - is name collision in reducer and also instead of merge you could use forkJoin to execute all requests in parallel.
this.store.dispatch(loadNotificationMobileCarriers()) - does it need requestParameter too?
Please try to code below, if it doesn't work - please check console log if it prints all 3 done and let me know in the comments below.
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
const notificationSettingsData$ =
this.store.pipe(
select(areNotificationSettingsLoaded),
tap(flag => {
if (!this.loadingNotifications && !flag) {
this.loadingNotifications = true;
this.store.dispatch(loadNotificationSettings({requestParameter: this.notificationsRequest}));
}
}),
filter(flag => flag),
first(),
finalize(() => {
this.loadingNotifications = false;
}),
);
const contactInfoData$ = this.store.pipe(
select(areContactInfoLoaded),
tap(flag => {
if (!this.loadingContactInfo && !flag) {
this.loadingContactInfo = true;
this.store.dispatch(loadContactInfo({requestParameter: this.notificationsRequest}));
}
}),
filter(flag => flag),
first(),
finalize(() => {
this.loadingContactInfo = false;
}),
);
const noificationMobileCarriers$ = this.store.pipe(
select(areNotificationMobileCarriersLoaded),
tap(flag => {
if (!this.loadingMobileCarriers && !flag) {
this.loadingMobileCarriers = true;
this.store.dispatch(loadNotificationMobileCarriers());
}
}),
filter(flag => flag),
first(),
finalize(() => {
this.loadingMobileCarriers = false;
}),
);
return forkJoin({
contactInfoData: contactInfoData$.pipe(tap(() => console.log('contactInfoData$ done'))),
notificationSettingsData: notificationSettingsData$.pipe(tap(() => console.log('notificationSettingsData$ done'))),
noificationMobileCarriers: noificationMobileCarriers$.pipe(tap(() => console.log('noificationMobileCarriers$ done'))),
});
}