Ionic 3/Google Distance Matrix API - google-maps

The basic idea I am trying to execute is:
I have a list of branches, each having lat, lng attributes in them, which contain latitude, and longitude. I want to use the Google Distance Matrix API to find the distance and duration to each one of them.
This is my code..
Branches.ts (page which shows all the branches, where I also want to show the distance and duration)
//other imports
import { TravelDetailsProvider } from '../../providers/travel-details/travel-details';
#IonicPage()
#Component({
selector: 'page-branches',
templateUrl: 'branches.html',
})
export class BranchesPage {
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public branchProvider: BranchProvider,
public travelProvider: TravelDetailsProvider
){
}
ionViewDidLoad() {
this.getBranches();
}
getBranchDistanceAndTime(){
for(let branch of this.allBranches){
this.travelProvider.getTravelDetails(branch);
}
}
}
I know that I should probably assign the value which is returned to a variable on my branches.ts and iterate them on the view but for some reason I cannot get a value to return from the getTravelDetails() method
Travel-details.ts
declare var google;
var service = new google.maps.DistanceMatrixService();
#Injectable()
export class TravelDetailsProvider {
private travelDetailsObject: any = {};
constructor(public http: HttpClient) {
}
getTravelDetails(branch){
service.getDistanceMatrix(
{
origins: [new google.maps.LatLng(6.870128,79.880340)],
destinations: [new google.maps.LatLng(branch.lat, branch.lng)],
travelMode: 'DRIVING'
}, this.callback);
}
callback(response, status) {
let travelDetailsObject;
if (status == 'OK') {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
var element = results[j];
var distance = element.distance.text;
var duration = element.duration.text;
var from = origins[i];
var to = destinations[j];
travelDetailsObject = {
distance: distance,
duration: duration
}
}
}
this.travelDetailsObject = travelDetailsObject;
}
}
}
When I run this code, I get an error: Uncaught TypeError: Cannot set property 'travelDetailsObject' of null
After doing some research, I ran into this github issues page. I'm wondering if this is the problem I am having
https://github.com/rollbar/rollbar.js/issues/344
Thanks for any help

You may try
constructor() {
this._mx = new google.maps.DistanceMatrixService();
}
getDistanceMatrix(req: DistanceMatrixRequest): Observable<DistanceMatrixResponse> {
return Observable.create((observer) => {
this._mx.getDistanceMatrix(req, (rsp, status) => {
// status checking goes here
observer.next(rsp);
observer.complete();
});
});
}

Related

I'm trying to create a memory game where an expanding list of numbers is shown in ionic and angular and the user has to type in the answer

The way that I am doing it is that I want each of the numbers to appear then disappear. I have tried a lot of options but only the last number ends up showing when there are two or more numbers in the array. I suspect it has something to do with the for loop, but there does not seem to be a way around it.
Here is my typescript code for the generate numbers function:
generateNumbers() {
let numbersArray = new Promise<number[]>((resolve, reject) => {
let numberArray: number[] = []
for (let i = 0; i < this.level; i++) {
this.animationCtrl.create()
.addElement(this.currentNum.nativeElement)
.duration(500)
.iterations(1)
.fromTo('opacity', '1', '0.05').play()
.then(func => {
let randomnum = Math.floor(Math.random() * 9)
numberArray.push(randomnum)
this.currentIndex = i
this.currentNumber = randomnum
this.parsedCurrentNumber = JSON.parse(JSON.stringify(this.currentNumber))
}).then(func => {
this.animationCtrl.create()
.addElement(this.currentNum.nativeElement)
.duration(500)
.iterations(1)
.fromTo('opacity', '0.05', '1').play()
}).then(func => {
if (i === this.level - 1) {
resolve(numberArray)
}
})
}
})
return numbersArray
}
Here are my variable declarations and injections:
#ViewChild('currentNumber', { read: ElementRef, static: true}) currentNum: ElementRef;
level: number = 1;
levelExp: number = 1;
gameHasBegun = false;
paused = false;
numbersArray: number[] = [];
answer: string;
wrongcount: number = 0;
wrong = false;
lost = false;
currentIndex: number = 0
currentNumber: number;
parsedCurrentNumber: string;
constructor(
private router: Router,
private menu: MenuController,
private animationCtrl: AnimationController ) { }
Here is how I call my generate function:
this.generateNumbers().then(
(val) => this.numbersArray = val
)
Here is my HTML Code for the part where the numbers should be shown, but instead only one number is shown when I have two or more numbers in my array:
<ion-content #currentNumber>
<ion-label class="ion-text-center" >
<h1>{{ parsedCurrentNumber }}</h1>
</ion-label>
</ion-content>
Look at the following stackblitz.
https://stackblitz.com/edit/ionic-79e1rn
You basically need to loop through your array with a timeout.
ionViewDidEnter(){
this.runSeries(0);
}
runSeries(i){
if(i < this.nums.length){
setTimeout(() => {
this.lastNum = this.nums[i];
i++;
this.runSeries(i);
}, 1000)
}
}
and bind lastNum in your template.

Angular how to push nested reactive form json ids in formArray

I want to submit an album gallery with eventTitle and multiple images. I am able to upload multiple images with eventTitle. It might be silly ask but I am new in angular and this is my first project on this. Stuck with this problem. Any reference/document would a big help.
While submitting the form, I just need to pass array of ids of images and eventTitle. My json looks like below:
{
"eventTitle": "Event to be celebrate - Happy New Year..!!",
"image": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
}
Problem is here that I am not able to push the array of ids. Only last uploaded image's id is getting push. notable to loop the ids and push into formArray. Can someone please help me how can loop the ids of all uploaded images?
// my Gallery component ts file:
constructor(private fb: FormBuilder,
private http: HttpClient,
private gallaryService: GallaryService,
private fileService: FileService,
private renderer: Renderer2) {
this.gallaryForm = this.fb.group({
eventTitle: [''],
image: this.fb.array([])
});
this.addGallaryImages();
}
ngOnInit() {
}
initSocialProfiles() {
return this.fb.group({
id: ['']
});
}
addGallaryImages() {
const control = this.gallaryForm.controls.image as FormArray; // how to loop it ids of array
const addrCtrl = this.initSocialProfiles();
control.push(addrCtrl);
console.log(addrCtrl);
}
gallaryFormSubmit() { //submitting the form
if (this.gallaryForm.valid) {
const gallaryFormData = this.gallaryForm.value;
gallaryFormData.image = [];
gallaryFormData.image[0] = {};
gallaryFormData.image[0].id = this.imageId;
this.gallaryService.saveGallaryForm(gallaryFormData).subscribe((response) => {
console.log(response);
// this.dialog.closeAll();
alert('New Gallary has been added...!');
});
}
}
onSelectedFile(event){
if (event.target.files.length > 0){
const image = event.target.files[0];
const formData = new FormData();
formData.append('file', image);
this.fileService.saveFile(formData).subscribe(
res => {
console.log(res);
if (res){
this.uploadError = '';
this.imageId = res.id;
const li: HTMLLIElement = this.renderer.createElement('li');
const img: HTMLImageElement = this.renderer.createElement('img');
img.src = res.path;
this.renderer.addClass(img, 'image');
const a: HTMLAnchorElement = this.renderer.createElement('a');
a.innerText = 'Remove';
this.renderer.addClass(a, 'delete-btn');
// a.addEventListener('click', this.deleteProductImage.bind(this, res.response.filename, a));
this.renderer.appendChild(this.image.nativeElement, li);
this.renderer.appendChild(li, img);
this.renderer.appendChild(li, a);
}
else {
this.uploadError = res.massage;
}
},
err => this.error = err
);
}
}
Gallery Service:
saveGallaryForm(gallary){
return this.http.post<any>('http://localhost:8080/gallary/save', gallary)
.pipe(
retry(1),
catchError(this.errorHandl)
);
}
[![ In below console log, last uploaded image id is getting push. I need all uploaded image ids in this array.][1]][1]

HTML error when Passing data from Parent component to Child Component in Angular

In here i am passing data from parent to child and trying to draw a graph by using those data. This is my graph drawing method which is in child component createGraph(divName, chartDataInfo).In here i am trying to draw 2 charts.
export class ViewComponent implements OnInit{
lineChart = ['line_chart1', 'line_chart2', 'line_chart3'];
value = ['33.5M', '67.9M', '90.9M', '09.9M'];
names = ['Bookings', 'Modifications','cancellations', 'Revenue' ];
bookingInfo = [];
mdfInfo = [];
ngOnInit() {
this.getInfo();
this.getBookingInfo();
}
getBookingInfo() {
const params = [];
params.push({code: 'dateType', name: 'BOOKING'});
params.push({code: 'fromDate', name: '2019-01-01'});
params.push({code: 'toDate', name: '2019-12-31'});
this.ServiceHandler.getTxnInfo([], params).subscribe(
bookings => {
this.bookingInfo = bookings.responseObj.txnValues;
console.log(this.bookingInfo);
});
}
getInfo(){
const params = [];
params.push({code: 'fromDate', name: '2019-01-01'});
params.push({code: 'toDate', name: '2019-12-31'});
this.ServiceHandler.getMdfInfo([], params).subscribe(
modifications => {
this.mdfInfo = modifications.responseObj.txnValues;
this.modificationTtcSum = modifications.responseObj.ttcSum;
});
}
}
This is my dashboard.component.html
<app-chips [lineChart]="lineChart[0]" [value] = "value[0]" [name] = "names[0]" [mdfInfo] = "mdfInfo"></app-summary-chips>
<app-chips [lineChart]="lineChart[1]" [value] = "value[1]" [name] = "names[1]" [bookingInfo] = "bookingInfo"></app-summary-chips>
This my child component. Therefore i have called this.createGraph(this.lineChart, this.mdfInfo); 2 times inside ngOnChanges(changes: SimpleChanges).But when i do like that both charts are not coming.When i call this.createGraph(this.lineChart, this.mdfInfo) only once inside ngOnChanges(changes: SimpleChanges) and at the same time i use only <app-summary-chips> selector only once then one chart is drawing. What is the reason for it?
export class ChipsComponent implements OnInit {
#Input('lineChart') lineChart: string;
#Input('value') value: string;
#Input('name') name: string;
#Input() bookingInfo = [];
#Input() mdfInfo = [];
ngOnChanges(changes: SimpleChanges) {
console.log(this.bookingInfo);
console.log(this.mdfInfo );
this.createGraph(this.lineChart, this.mdfInfo);
this.createGraph(this.lineChart, this.bookingInfo);
}
createGraph(divName, chartDataInfo) {
am4core.useTheme(am4themesAnimated);
const chartNameChartTTV = am4core.create(divName, am4charts.XYChart);
chartNameChartTTV.width = am4core.percent(100);
chartNameChartTTV.height = am4core.percent(100);
console.log(this.bookingInfo);
chartNameChartTTV.padding(0, 0, 0, 0);
chartNameChartTTV.data = [];
for (let i = 0; i < chartDataInfo.length; i++) {
const booking = chartDataInfo[i];
console.log(booking);
chartNameChartTTV.data.push({date: booking.time, value: booking.ttc});
}
chartNameChartTTV.colors.list = [
am4core.color('rgba(4, 69, 142, 1)'),
];
// Create axes
const dateAxis = chartNameChartTTV.xAxes.push(new am4charts.DateAxis());
const valueAxis = chartNameChartTTV.yAxes.push(new am4charts.ValueAxis());
valueAxis.renderer.grid.template.disabled = true;
valueAxis.renderer.labels.template.disabled = true;
dateAxis.renderer.grid.template.disabled = true;
dateAxis.renderer.labels.template.disabled = true;
// Create series
const series = chartNameChartTTV.series.push(new am4charts.LineSeries());
series.dataFields.valueY = 'value';
series.dataFields.dateX = 'date';
series.tooltipText = '{value';
series.fillOpacity = 1;
series.strokeWidth = 2;
series.minBulletDistance = 15;
}
}
This is my chips.component.html
<div class="l-content-wrapper c-summary-chip oh" >
<div class="c-summary-chip__value">{{value}}</div>
<div class="c-summary-chip__txt">{{name}}</div>
<div id= "{{lineChart}}" class="c-summary-chip__graph ">
</div>
</div>
Try wrapping your code inside an if check
createGraph(divName, chartDataInfo) {
if (divName && chartDataInfo.length) {}
}
As ngOnChanges is an async event there is no guarantee that when you call the function inside changes all parameters will be present.
Also for debugging give console.log like this.
createGraph(divName, chartDataInfo) {
console.log(divName, chartDataInfo);
if (divName && chartDataInfo) {}
}
That way you can confirm all required data is there when you call the function.
Inside ngOnChanges hook use SimpleChanges to track your input parameters.
If params present, then call your function.
ngOnChanges(changes: SimpleChanges): void {
if (changes['divName'] && changes['chartDataInfo']) {
this.createGraph(changes['divName'].currentValue, changes['chartDataInfo'].currentValue);
}
}
Take a look please https://dev.to/nickraphael/ngonchanges-best-practice-always-use-simplechanges-always-1feg

How to fix DOMException error in google chrome?

I work with sounds in a browser game. I wrote sound manager. everything works fine, but not in Google chrome. I handled the error "uncaught (in promise) domexception", after the sounds were played in 50 percent of cases, in other cases it returns the error DOMException. What could be the problem?
export class AudioFile{
private audio: HTMLAudioElement;
private fileMP3: string;
private fileOGG: string;
private volume = 1;
private loop = false;
constructor(MP3:string, OGG:string) {
this.audio = new Audio();
this.fileMP3 = MP3;
this.fileOGG = OGG;
this.audio.canPlayType('audio/mpeg') ? this.audio.src = this.fileMP3 : this.audio.src = this.fileOGG;
this.audio.load();
this.audio.volume = this.volume;
this.audio.loop = this.loop;
}
public play() {
this.audio.currentTime = 0;
const playPromise = this.audio.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
})
.catch(error => {
console.log(error);
});
}
}
public stop() {
this.audio.pause();
}
}
``````````````sound manager`````````````
export class SoundManager {
private sounds = new Map();
private static _soundManager: SoundManager;
constructor(){
if (SoundManager._soundManager) {
throw new Error("Instantiation failed: "+
"use Singleton.getInstance() instead of new.");
}
SoundManager._soundManager = this;
}
public static get instance(): SoundManager {
if (this._soundManager)
return this._soundManager;
else
return this._soundManager = new SoundManager();
}
preload() {
const pathMP3 = SoundConfig.PATHMP3;
const pathOGG = SoundConfig.PATHOGG;
for (const item in SoundConfig.SOUNDS) {
const name = SoundConfig.SOUNDS[item].NAME;
this.sounds.set(name, new AudioFile(pathMP3 + name + '.mp3', pathOGG + name + '.ogg'));
}
}
getSound(id: string): AudioFile {
return this.sounds.get(id);
}
}
Thank you spendr.
error: DOMException
code: 0
message: "play() failed because the user didn't interact with the document first.
Game runs through the iframe and I was needed to add a feature policy for autoplay.
<iframe src="..." allow="autoplay">
The article that helped me in solving the problem

Display only the first elements of Object.keys

I have made a JSON Request so i can bring the Objects in Angular2. But I want to display only the first 15 elements and then if it works repeat the same process on InfiniteScroll. So this is one of my code.
setList(informes) {
if (informes) {
for (let id of Object.keys(informes)){
this.count = 0;
for (let i = 0; i < 15; i++) {
let node = informes[id];
this.informes.push(node[this.count]);
console.log (id);
this.count++;
}
}
}
}
Obviously It doesn't work, it keeps giving me all elements like 15 times each. I know that but on the other hand if i make the opposite.
setList(informes) {
if (informes) {
for (let i = 0; i < 15; i++) {
for (let id of Object.keys(informes)){
let node = informes[id];
this.informes.push(node[this.count]);
console.log (id);
}
this.count++
}
}
}
It counts the number of nodes in total.
What i want is to display only the first 15 elements. And then repeat the code in my other function infiniteScroll (I will do that by myself, it works).
Any suggestion will be appreciated.
UPDATE:
Here's the constructor:
constructor(public navCtrl: NavController, public navParams: NavParams, public nav: NavController, public http: Http, public sanitizer: DomSanitizer) {
this.dataUrl = 'https://myurl.com/ionic/'; //example
if (this.dataUrl) {
this.http.get(this.dataUrl)
.map(res => res.json())
.subscribe(informes => this.setList(informes));
}
}
UPDATE 2:
The code works well.
I had to modify some things to make it work. I will update the script so if it could help someone.
setList(informes) {
if (informes) {
let ids = Object.keys(informes);
ids.forEach((id, index) => {
if(index < 15){
let node = informes[id];
this.informes.push(node);
this.count++;
console.log(this.count);
}
});
}
}
goToNodeInformes(node){
this.navCtrl.push(NodeInformesPage, {'node':node.nid});
}
doInfinite(infiniteScroll, informes) {
informes = this.informes;
setTimeout(() => {
let ids = Object.keys(informes);
ids.forEach((id, index) => {
if(index < 15){
let node = informes[id];
this.informes.push(node);
this.count++;
console.log(this.count);
}
});
infiniteScroll.complete();
}, 500);
}
}
I will figure what i have to do for not repeating the same nodes (will update) but the counter works!!!
I think you are looking for something like this :
let keys = Object.keys(informes);
keys.foreach((key, index) => {
if(index < 15){
let node = informes[key];
this.informes.push(node);
console.log(informes[key]);
}
});