How to get rid of this hack - dom

I want to modify the class so that it does not use the ApplicationRef. In other words how to get hold of main app not using app ref.
#Injectable()
export class ToastsManager {
container: ComponentRef<any>;
private options = {
autoDismiss: true,
toastLife: 1000
};
private index = 0;
container: ComponentRef<any>;
private options = {
autoDismiss: true,
toastLife: 1000
};
private index = 0;
constructor(private resolver: ComponentResolver,
private appRef: ApplicationRef,
#Optional() #Inject(ToastOptions) options) {
if (options) {
Object.assign(this.options, options);
}
}
show(toast: Toast) {
if (!this.container) {
// a hack to get app element in shadow dom
let appElement: ViewContainerRef = new ViewContainerRef_(this.appRef['_rootComponents'][0]._hostElement);
this.resolver.resolveComponent(ToastContainer)
.then((factory: ComponentFactory<any>) => {
this.container = appElement.createComponent(factory);
this.setupToast(toast);
});
} else {
this.setupToast(toast);
}
}
I try with the #ViewChild but it does not work.

You could do with ApplicationRef what Brandon Roberts demonstrates in https://github.com/angular/angular/issues/4112#issuecomment-139381970 to get a reference to the Router in CanActivate().
Probably better would be a shared service
#Injectable()
export class Shared {
appRef = new BehaviorSubject();
setAppRef(appRef:ApplicationRef) {
this.appRef.emit(appRef);
}
}
export class ToastsManager {
constructor(private resolver: ComponentResolver,
private appRef: ApplicationRef,
shared:Shared,
#Optional() #Inject(ToastOptions) options) {
shared.setAppRef(appRef);
}
}
export class OtherClassThatNeedsAppRef {
constructor(shared:Shared) {
shared.appRef.subscribe(appRef => this.appRef = appRef);
}
}

Related

How to remove the surround IIFE from #babel/template?

I'm trying to convert some Ember codes to React. Here is what I want to transform.
From
export default Ember.Component.extend(({
didInsertElement() { }
});
To
export default class MyComponent extends React.Component {
componentDidMount() { }
}
I write a babel plugin, and try to replace the Ember.Component.extend call with an AST node which is produced by template method. Here is the code snippet.
babel-plugin
const { default: template } = require("#babel/template");
const code = `class TestComponent extends React.Component {
componentDidMount() { }
}`;
let classDeclarationNode = template.ast(code);
module.exports = function ({ types: t }) {
return {
visitor: {
CallExpression(path) {
if (!path.getSource().startsWith("Ember.Component.extend")) {
return;
}
path.replaceWith(classDeclarationNode);
}
}
};
};
Output
export default (function () {
class TestComponent extends React.Component {
componentDidMount() {}
}
})();
Instead of the expected code I wrote above, I get the ClassDeclaration statement surrounded with IIFE. Is there any way to remove the IIFE?
I have struggled with the problem one whole day, but have no way to resolve it.
BTW, I also tried parseExpression method, but still can't get what exactly I want.
babel-plugin
const { parseExpression } = require('#babel/parser');
const code = `class TestComponent extends React.Component {
componentDidMount() { }
}`;
let expression = parseExpression(code);
module.exports = function ({ types: t }) {
return {
visitor: {
CallExpression(path) {
if (!path.getSource().startsWith("Ember.Component.extend")) {
return;
}
path.replaceWith(expression);
}
}
};
};
Output
export default (class TestComponent extends React.Component {
componentDidMount() {}
});
It's quite close to the correct code, except an extra pair of (). Is there any way to generate the pure class declaration?
Thanks for the help of loganfsmyth on Slack, the problem was finally resolved. I should replace the node of whole export default but not only the CallExpression. Here is the code.
babel-plugin
const { default: template } = require("#babel/template");
const code = `export default class TestComponent extends React.Component {
componentDidMount() { }
}`;
let rootNode = template.ast(code);
module.exports = function ({ types: t }) {
return {
visitor: {
ExportDefaultDeclaration(path) {
let isMatchedNode = (path.node.declaration &&
t.matchesPattern(path.node.declaration.callee, "Ember.Component.extend"));
if (!isMatchedNode) {
return;
}
path.replaceWith(rootNode);
}
}
};
};
Output
export default class TestComponent extends React.Component {
componentDidMount() {}
}

Cannot read property forEach of undefined

The title of this question is just the error I am currently receiving, but what I really need help with is understanding observables and API calls. For whatever reason, I just haven't been able to get a good grasp of this concept, and I am hoping that someone might have an explanation that will finally click.
I am trying to create a new Angular service that retrieves JSON from an API. I then need to map the response to a model. Due to weird naming conventions, job descriptions and job requirements are used interchangeably here. Here is my service class.
import { CommunicationService } from './communication.service';
import { AiDescription } from '../models/ai-description.model';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class AiDescriptionService {
requirements: Observable<AiDescription[]>;
private aiDescriptionUrl: string = '/api/core/company/jobdescriptions';
private dataStore: {
requirements: AiDescription[]
};
private _requirements: BehaviorSubject<AiDescription[]>;
private emptyRequestParams = {
"company_id": "",
"carotene_id": "",
"carotene_version": "",
"city": "",
"state": "",
"country": ""
};
readonly caroteneVersion: string = "caroteneV3";
constructor(
private communicationService: CommunicationService
) {
this.dataStore = { requirements: [] };
this._requirements = new BehaviorSubject<AiDescription[]>([]);
this.requirements = this._requirements.asObservable();
}
LoadRequirements(params: Object) {
this.communicationService.postData(this.aiDescriptionUrl, params)
.subscribe(res => {
let jobDescriptions = [];
jobDescriptions = res.jobdescriptions;
jobDescriptions.forEach((desc: { id: string; description: string; }) => {
let aiDescription = new AiDescription();
aiDescription.id = desc.id;
aiDescription.description = desc.description;
});
this.dataStore.requirements = res;
this._requirements.next(Object.assign({}, this.dataStore).requirements);
});
}
CreateRequest(
companyID : string,
caroteneID : string,
city: string,
state: string,
country: string
): Object {
let newRequestParams = this.emptyRequestParams;
newRequestParams.company_id = companyID;
newRequestParams.carotene_id = caroteneID;
newRequestParams.carotene_version = this.caroteneVersion;
newRequestParams.city = city;
newRequestParams.state = state;
newRequestParams.country = country;
this.LoadRequirements(newRequestParams);
return this.dataStore;
}
}
The postData() function being called by this.communicationService is here:
postData(url: string, jobInformation: any): Observable<any> {
const start = new Date();
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
const body = JSON.stringify(jobInformation);
const options = { headers };
return this.http.post(url, body, options)
.catch(err => Observable.throw(err))
.do(() => {
this.analyticsLoggingService.TrackTiming('JobPostingService', 'PostSuccess', new Date().getTime() - start.getTime());
}, () => {
this.analyticsLoggingService.TrackError('JobPostingService', 'PostFailure');
});
}
I didn't write the postData function, and I would not be able to modify it. When running a unit test, I am getting this error: "TypeError: Cannot read property 'forEach' of undefined".
But more than simply fixing the error, I am really trying to get a better understanding of using Observables, which is something I haven't been able to get a good understanding of from other sources.
In your example, I recommend replacing any and Object with explicitly defined models.
Here's an example for Angular 8 for Subscription, Promise, and Observable API calls. You can get more info here: https://angular.io/tutorial/toh-pt6.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs';
import { User } from './user.model';
#Injectable({ providedIn: 'root' })
export class UserService {
users: User[];
authHeaders = new HttpHeaders()
.set('Content-Type', 'application/json');
constructor(
private readonly http: HttpClient
) { }
getUsers() {
this.http.get(`https://myApi/users`, { headers: this.authHeaders })
.subscribe(
(data: User[]) => {
this.users = data;
}, (error: HttpErrorResponse) => { /* handle error */ });
}
async getUserPromise(userID: number): Promise<User> {
const url = `https://myApi/users/${userID}`;
return this.http.get<User>(url, { headers: this.authHeaders })
.toPromise();
}
getUserObservable(userID: number): Observable<User> {
const url = `https://myApi/users/${userID}`;
return this.http.get<User>(url, { headers: this.authHeaders });
}
}
I like to keep my class models in separate files. This example would have user.model.ts with content like:
export class User {
constructor(
public id: number,
public username: string,
public displayName: string,
public email: string
) { }
}
I've not included authentication headers or error handling for brevity; however, you might want to add those as needed.

Cannot use namespace 'NavController'',NgZone',Google,as a type

I am new to ionic app developer, and I am facing this issue: when I open one Component, it is showing error messages, i.e, cannot use namespace as type ionic(NgZone,NavController and googlePlus). Why am I getting this error message?
Here is my code:
export class HomePage {
userProfile: any = null;
zone: NgZone;
constructor(public navCtrl: NavController, private googlePlus: GooglePlus) {
this.zone = new NgZone({});
firebase.auth().onAuthStateChanged( user => {
this.zone.run( () => {
if (user){
this.userProfile = user;
} else {
this.userProfile = null;
}
});
});
}
}
Maybe you forgot to update your project's package. Please try with:
npm install / npm update
Try the Below code : ( The NgZone object can be created as below )
export class HomePage {
userProfile: any = null;
zone: NgZone;
constructor(public navCtrl: NavController, private googlePlus: GooglePlus, private ngZone: NgZone) {
this.zone = ngZone;
firebase.auth().onAuthStateChanged( user => {
this.zone.run( () => {
if (user){
this.userProfile = user;
} else {
this.userProfile = null;
}
});
});
}
}

MongoDB Stitch and Angular 6 application

I'm creating a service for my MongoDB Stitch connections and I'm having an issue where if I refresh my page I get an error saying:
client for app 'xyxyxyxyxyxy' has not yet been initialized
And when I try to initialize it I get an error saying it has already been initialized.
client for app 'xyxyxyxyxyxy' has already been initialized
Here is my service.
import { Injectable } from '#angular/core';
import { Stitch, RemoteMongoClient, UserApiKeyCredential} from 'mongodb-stitch-browser-sdk';
#Injectable({
providedIn: 'root'
})
export class AnalyticsService {
client: any;
credential: UserApiKeyCredential;
db: any;
constructor() {
console.log(Stitch.hasAppClient('xyxyxyxyxyxy'));
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
console.log('here');
this.client = Stitch.initializeAppClient('xyxyxyxyxyxy');
//this.client = Stitch.getAppClient('xyxyxyxyxyxy');
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}
login() {
this.credential = new UserApiKeyCredential('APIKEY');
this.client.auth.loginWithCredential(this.credential)
.then(authId => {
console.log(authId);
});
}
logout() {
this.client.auth.logout()
.then(resp => {
console.log(resp);
});
}
insertData(collectionName: string, data: {}) {
this.db.collection(collectionName).insertOne(data)
.then(resp => {
console.log(resp);
});
}
getData(collectionName: string) {
this.db.collection(collectionName).find({})
.asArray().then(resp => {
console.log(resp);
});
}
}
Change the constructor to be like this and it fix the issue.
constructor() {
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
this.client = Stitch.defaultAppClient;
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}

Ionic Searchbar with PHP API

It works but i anot getting the results it should sort. I am getting the same results regardless what i type in the searchbar
I want it to sort like autocomplete. to show results of what i type in the search bar
search.ts
#Component({ selector: "page-search", templateUrl: "search.html" })
export class SearchPage {
filter: string = '';
public userDetails: any;
public resposeData: any;
public dataSet: any;
public userSet: any;
public mediaSet: any;
public noRecords: boolean;
userPostData = {
uid: "",
token: "",
username: "",
bio: ""
};
constructor(
public common: Common,
public navCtrl: NavController,
public app: App,
public menu: MenuController,
public authService: AuthService,
public http: Http,
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen
) {
this.initializeItems();
this.mostmediaList();
}
initializeItems() {
return this.userPostData;
}
getItems(ev: any) {
this.initializeItems();
let val = ev.target.value;
if (val && val.trim() != '') {
this.authService.postData(this.userPostData, "userGroupSearch").then(
result => {
this.resposeData = result;
if (this.resposeData.allArtistsData) {
this.userSet = this.resposeData.allArtistsData;
console.log(this.userSet);
} else {
console.log("No access");
}
},
);
}
}
Since your code is wrapped into
if (this.resposeData.items) {
//some code
}
we know for sure that this.resposeData is not an array, since it has an items member (otherwise your code inside the if would not be executed and hence you would not get an error as in the case we have).
Since you call the parameter items at
this.userSet = this.resposeData.filter((items) => {
//some code
};
it is safe to assume that you wanted to filter this.resposeData.items instead of this.resposeData. So, you will need to make sure it is an array at the if
if (this.resposeData.items && Array.isArray(this.resposeData.items)) {
//some code
}
and filter this.resposeData.items instead of this.resposeData:
this.userSet = this.resposeData.items.filter((items) => {
//some code
};