Making moment injectable in angular2 - angularjs

I would like to make moment to be injectable through out my app.
I just started learning ng2 and couldn't find this type of usage in the docs.
Here is what I have in my app.module.ts:
import {BrowserModule} from '#angular/platform-browser';
import {NgModule} from '#angular/core';
import {FormsModule} from '#angular/forms';
import {HttpModule} from '#angular/http';
import {AppComponent} from './app.component';
import * as moment from 'moment';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [{provide: 'moment', useValue: moment}],
bootstrap: [AppComponent]
})
export class AppModule {
}
and here is the component:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
title = 'app works!';
constructor(private moment) {
this.title += this.moment;
}
}
there is this error:
Uncaught Error: Can't resolve all parameters for AppComponent:
How should this be done correctly?
UPDATED MODULE
const moment = new OpaqueToken('moment');
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [{provide: moment, useValue: moment}],
bootstrap: [AppComponent]
})
export class AppModule {
}
UPDATED COMPONENT
import { Component } from '#angular/core';
import * as moment from 'moment';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
title = 'app works!';
constructor(private moment: moment) {
this.title += this.moment()
}
}
There is an error on this line constructor(private moment: moment) which tells that: Cannot find name 'moment'.

Moment by itself is not an injectable for Angular2. However it can be wrapped inside one.
Plunker Demo
moment.service.ts
import { Injectable } from '#angular/core';
import * as m from 'moment';
#Injectable()
export class MomentService {
moment = m;
}
app.module.ts
import { MomentService } from './moment.service';
#NgModule({
providers: [MomentService]
...
app.component.ts
import { MomentService } from './moment.service';
export class AppComponent {
constructor(private ms: MomentService){
console.log('Moment:' + this.ms.moment("20111031", "YYYYMMDD").toString());
}
}
Not perfect, but works.

You need to use OpaqueToken which will allowed you to create a string based token. I'd just suggest you to change the name of moment to avoid thr collision with moment variable of library.
// You can place this is some file, so that you can export it.
export const Moment = new OpaqueToken('moment');
and then you could use
providers: [{provide: MomentStatic, useClass: moment}],
You can go through this article for more details
While using dependency include it in a Component constructor.
constructor(private moment: MomentStatic)

Moment doesn't have to be injected, it is a library that you can "just use". It's enough to import it in your typescript file now you can use moment's features.

If you are loading moment.js so it is available globally how about wrapping it with a service which you can then inject throughout your app?
import {Moment} from '../../node_modules/moment';
import { Injectable } from '#angular/core';
declare var moment: any;
#Injectable()
export class MomentService {
constructor() { }
get(): Moment {
return moment;
}
}
I this way you get the TS IntelliSense when you are coding (at least I do in VSCode) and you can handle mocking moment for testing easily too.

The best workaround that I have come to find is to create a wrapper service, but also expose some of the most common methods directly on the service itself using getters:
import { Injectable } from '#angular/core';
import * as moment from 'moment';
/**
* A wrapper for the moment library
*/
#Injectable()
export class MomentService {
/**
* Creates and returns a new moment object with the current date/time
*/
public moment() { return moment(); }
// expose moment properties directly on the service
public get utc() { return moment.utc; }
public get version() { return moment.version; }
public get unix() { return moment.unix; }
public get isMoment() { return moment.isMoment; }
public get isDate() { return moment.isDate; }
public get isDuration() { return moment.isDuration; }
public get now() { return moment.now; }
}

Not sure if this helps now but you need something similar (not tested) to the following to make it work in your service.
import { Inject } from '#angular/core';
constructor(#Inject(moment) private moment) {
this.title += this.moment()
}
The key bit is the #Inject

Related

How do I fix 'this.http.get is not a function' error?

I've been trying to create a language localization app in Ionic. The idea is to setup the translation service in the app.component (to make it available throughout the app) and then use it in a tab (for ex: tab2.html).
I receive an error as follows:
TypeError: this.http.get is not a function
at TranslateHttpLoader.push../node_modules/#ngx-translate/http-loader/fesm5/ngx-translate-http-loader.js.TranslateHttpLoader.getTranslation (ngx-translate-http-loader.js:27)
at TranslateService.push../node_modules/#ngx-translate/core/fesm5/ngx-translate-core.js.TranslateService.getTranslation (ngx-translate-core.js:738)
at TranslateService.push../node_modules/#ngx-translate/core/fesm5/ngx-translate-core.js.TranslateService.retrieveTranslations (ngx-translate-core.js:713)
at TranslateService.push../node_modules/#ngx-translate/core/fesm5/ngx-translate-core.js.TranslateService.setDefaultLang (ngx-translate-core.js:629)
at new AppComponent (app.component.ts:18)
at createClass (core.js:22062)
at createDirectiveInstance (core.js:21931)
at createViewNodes (core.js:23157)
at createRootView (core.js:23071)
at callWithDebugContext (core.js:24079)
Ionic info is as follows:
ionic (Ionic CLI) : 4.9.0
ionic Framework : #ionic/angular 4.0.0
#angular-devkit/build-angular : 0.12.3
#angular-devkit/schematics : 7.2.3
#angular/cli : 7.2.3
#ionic/angular-toolkit : 1.2.3
System:
NodeJS : v10.15.0 (C:\Program Files\nodejs\node.exe)
npm : 6.4.1
OS : Windows 10
The code files are as below.
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { TranslateModule, TranslateLoader } from '#ngx-translate/core';
import { TranslateHttpLoader } from '#ngx-translate/http-loader';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}
#NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy}
],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
import { Component } from '#angular/core';
import { Platform } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { TranslateService } from '#ngx-translate/core';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
constructor(
private translate: TranslateService,
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar
) {
translate.setDefaultLang('en');
this.initializeApp();
}
switchLanguage(language: string) {
this.translate.use(language);
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
}
tab2.module.ts
import { IonicModule } from '#ionic/angular';
import { RouterModule } from '#angular/router';
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { Tab2Page } from './tab2.page';
import { AppComponent } from './../app.component'
import { TranslateModule } from '#ngx-translate/core';
#NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
RouterModule.forChild([{ path: '', component: Tab2Page }]),
TranslateModule.forChild()
],
declarations: [Tab2Page]
})
export class Tab2PageModule {}
tab2.page.html
<ion-header>
<ion-toolbar>
<ion-title>
Tab Two
</ion-title>
<ion-content>
<h1 translate> Title </h1>
<div>
{{ 'Intro' | translate:user }}
</div>
<button (click)="switchLanguage('en')">en</button>
<button (click)="switchLanguage('hn')">hn</button>
</ion-content>
en.json
{
"Title": "Translation example",
"Intro": "Hello I am Arthur, I am 42 years old."
}
The expected result is The browser page for tab2 should have the text and 2 buttons for the language selection. On clicking, the text should get altered as expected. I am working on Ionic and Angular for the very first time and do not have a good grasp on how the syntax or general flow works. If the code has multiple errors, please let me know. Any help would be appreciated!
If you need access to a function from several places, consider putting it in a service.
I was unable to replicate the issue you mentioned using your code. However, I have created a sample application considering your requirements. You can follow it here
https://stackblitz.com/edit/internationalization-example
Explanation below
I have created a service
event-provider.service
import { EventEmitter } from '#angular/core';
export class EventProvider {
// Use currentLang to subscribe to the change event
public currentLang: EventEmitter<string> = new EventEmitter();
//Call this method from other components or pages to Change the language
setLang(val) {
this.currentLang.emit(val);
}
}
I'm consuming the created service in two places,
tab2.page.ts
export class Tab2Page implements OnInit {
constructor(private translate: TranslateService,private
eventProvider:EventProvider) { }
ngOnInit() {
}
switchLanguage(language: string) {
this.eventProvider.setLang(language);
}
}
app.component.ts
//Subscribe to language change in app component
this.eventProvider.currentLang.subscribe(lang => {
this.translate.use(lang);
});
Hope this helps.

Passing an array between pages in ionic 2

I'm new in Ionic 2 and I'm having troubles with passing data between pages. In my Home.ts file I have a global array that contains some numbers I calculated and i want to pass it to my Table.ts file, to show it in a HTML table with the *ngFor method.
this is the Function in Home.ts where i fill the array and try to push (i will skip the calculations, becacause i know they are correct).
`import { Component } from '#angular/core';
import { AlertController } from 'ionic-angular';
import { IonicPage,NavController, NavParams} from 'ionic-angular';
import {Table} from '../table/table';
export class HomePage {
averagesList: Array <number> =[];
constructor(public alerCtrl: AlertController,
public navCtrl: NavController,
public navParams: NavParams)
{}
Calculate(){
var Averages=[];
//Calculations on the 'Averages' Array
this.averagesList = Averages;
this.navCtrl.push(Table,this.averagesList);
}
}
So I try to print it in my Table.ts file but it gives me undefined result
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import {HomePage} from '../home/home';
#IonicPage()
#Component({
selector: 'page-table',
templateUrl: 'table.html',
})
export class Table{
constructor(public navCtrl: NavController, public navParams: NavParams) {
}
ionViewDidLoad() {
console.log(this.navParams.get('averagesList'));
}
}
I've tried to pass a let variable and it worked, so why doesn't it work with arrays?
Your mistake is using console.log(this.navParams.get('averagesList'));
Here 'averagesList' is the key.
To get it this way, you need to send as :
this.navCtrl.push(Table,{'averagesList' : this.averagesList});
Else:
if you directly send as
this.navCtrl.push(Table,this.averagesList);
You can retrieve value like so:
console.log(this.navParams.data);
you can use services to do so. Just like in angular2 you can import your service within the constructor and use the property like this.
import {OnInit} from '#angular/core';
import {someService} from ./somepath;
...
export class someClass implements OnInit{
let myTmpVar; //we will capture the shared data in this variable
constructor (private smService: someService){ ... }
ngOnInit{
this.myTmpVar = this.smService.SharedServiceData;
}
...
}
It's better to use service for passing nested data. In your case calculations object.
You can create messageService and listen to changes, something like below.
import {Injectable} from '#angular/core';
import {Observable} from 'rxjs';
import {Subject} from 'rxjs/Subject';
#Injectable()
export class LocalMsgService {
private subject = new Subject();
sendMessage(message) {
this.subject.next(message);
}
clearMessage() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
Which can be used in your home.ts and table.ts pages as follows
Home.ts
//other imports comes here
import {LocalMsgService} from 'services/localMsg';
#Component({
selector: 'home-component',
templateUrl: 'home.html'
})
export class HomePage {
constructor( private msgService: LocalMsgService) {
}
dataToPass() {
console.log(this.averagesList);
this.msgService.sendMessage(this.averagesList);
}
}
Table.ts
//other imports comes here
import {LocalMsgService} from 'services/localMsg';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'page-table',
templateUrl: 'table.html',
})
export class TablePage{
items: any;
subscription: Subscription;
constructor(
public localMsgService : LocalMsgService) {
this.subscription = this.localMsgService.getMessage().subscribe(msg => {
this.items = msg;
});
}
}

Angular 2 Communicating with <router-outlet> components

I have a search bar in a header component.
Beneath that, I have a "router-outlet" in that same component.
The search bar (input txtfield), once enter is pressed, needs to send the search string (event.target.value) to the component that resides within the router-outlet beneath it so that it can run a method to return the results.
I have no clue what the best way is to achieve this.
UPDATED with code..
app.component.html:
<div class="white-container">
<input name="searchStr" [(ngModel)]="searchStr" (keyup.enter)="searchCourse($event)">
</div>
<router-outlet></router-outlet>
app.component.ts:
import { Component, OnInit } from '#angular/core';
import { CourseService } from './services/courses.service';
import { Course } from './Course';
#Component({
selector: 'my-app',
templateUrl: 'app.component.html',
providers: [CourseService]
})
export class AppComponent implements OnInit {
constructor(private _courseService: CourseService) {
}
searchCourse(event) {
// the user search string here...
}
}
/course-listings/course-listings.component.ts:
import { Component, OnInit } from '#angular/core';
import { CourseService } from './services/courses.service';
import { Course } from './Course';
#Component({
selector: 'app-course-listings',
templateUrl: './course-listings.component.html',
styleUrls: ['./course-listings.component.css'],
providers: [CourseService]
})
export class AppComponent implements OnInit {
course: Course[];
constructor(private _courseService: CourseService) {
}
searchCourse(evt) {
// This works once it's fired...
this._courseService.findCourse(evt)
.subscribe(courses => {
this.course = courses;
});
}
}
/services/courses.service.ts:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class CourseService {
constructor(private _http:Http) {
}
getCourses(search) {
return this._http.get('/api/v1/courses/'+search)
.map(res => res.json());
}
}
FIX FOUND
Günter Zöchbauer was correct. I used a service w/ subscribe and observables to do it. Thanks.
An event.subscriber would be required in the constructor to pass to router-outlet.
Similar to the answer in this Angular 2 router event listener.
So, once the click is done, the subscriber event will be executed based on on the navigationend event, then the value can be accessed.

Ng2 <Component> is not a known element

I have browsed the other similar posts on stackoverflow, but have not found one that helps my cause, so here goes:
I am using angular2 based on the webpack "boiler-plate" from angular.io and included the routing bit.
I end up with this error even though the current setup is extremely minimal:
Unhandled Promise rejection: Template parse errors:
'Mathador' is not a known element:
1. If 'Mathador' is an Angular component, then verify that it is part of this module.
...
Here are the relevant code fragments
app/app.html
<main>
<h1>Mathador prototype</h1>
<router-outlet></router-outlet>
</main>
**app/app.routes.ts**
import { ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { HomeComponent } from './pages/home/home'
const appRoutes: Routes = [
{
path: '', component: HomeComponent
}
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
**app/app.ts**
import { Component } from '#angular/core';
import '../../public/css/styles.css';
#Component({
selector : 'my-app',
templateUrl : './app.html',
styleUrls : ['./app.scss']
})
export class AppComponent {
}
**app/app.module.ts**
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { routing } from './app.routes';
// core
import { AppComponent } from './app';
// pages
import { HomeComponent } from './pages/home/home';
// components
import { Mathador } from './components/mathador/mathador';
#NgModule({
imports: [
BrowserModule,
routing
],
declarations: [
AppComponent,
HomeComponent,
Mathador
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
**app/pages/home/home.ts**
import { Component } from '#angular/core';
#Component({
selector : 'my-home',
templateUrl : './home.html',
styleUrls : ['./home.scss']
})
export class HomeComponent {
constructor() {
// Do stuff
}
}
**app/pages/home/home.html**
<h1>home!</h1>
<Mathador></Mathador>
**app/components/mathador.html**
<div>transclusion succesfull!</div>
**app/components/mathador.ts**
// Importing core components
import {Component} from '#angular/core';
#Component({
selector : 'mathador',
templateUrl : './mathador.html'
})
export class Mathador {
constructor() { }
}

injecting service into CanActivate method of router Guard

before redirecting to page i am doing simple check using below service
import { Injectable,OnInit } from '#angular/core';
export class UserCheck {
private userState:boolean = false;
get isValid(): boolean{
return this.userState;
}
set isValid(usrstate){
this.userState = usrstate;
}
}
User check is service that set isValid to true or false in another service;
Assume that another service has set isValid to true, I am using providers to have sigle instance of UserCheck of usercheck service. But when guard actually get executed its not considering value that was set to true.
import { CanActivate,ActivatedRouteSnapshot,RouterStateSnapshot } from '#angular/router';
import { UserCheck } from './UserCheck' ;
import { Observable } from 'rxjs/Rx';
import { Injectable } from '#angular/core';
#Injectable()
export class UserDetailGuard implements CanActivate{
constructor (private usrstate:UserCheck){
console.log ("guard in action " + this.usrstate.isValid) }
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot) : Observable<boolean> | boolean{
console.log ("guard executed.. " + this.usrstate.isValid)
return this.usrstate.isValid;
}
}
As soon as page load value of this.usrstate.isValid can be seen to true in console log guard in action true in constructor code (see image), but when i actually navigate to page it does not consider value of this.usrstate.isValid and assign default value which is false.
app.module.js
Removed unnecessary code..
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { FormsModule,ReactiveFormsModule } from '#angular/forms';
import { routing } from './app.rotes';
import { UserCheck } from './UserCheck';
import { LogMessage } from './LogMessage';
import { AccessDenied } from './AccessDenied';
import { UserDetailGuard } from './UserDetailGuard'
#NgModule({
imports: [ BrowserModule,FormsModule,ReactiveFormsModule,routing,],
declarations: [ AppComponent,AccessDenied ],
bootstrap: [ AppComponent],
providers: [UserCheck,LogMessage,UserDetailGuard]
})
export class AppModule { }

Resources