using d3 geomap in Angular 9 - javascript

Trying to use geomap and build a map of USA that I can colour in based on values, but I can't get the map to draw, but it is there (well, the hook workers and data is put in the DIV, can even see the states, but its 0 size.
Started by installing the library
npm install d3-geomap
Then created an empty new component (TestDraw.component)
Only thing in the html is a div to point d3 to, so my test-draw.componenet.html looks like this
<div class="d3-geomap" id="map"></div>
Moved the USA.json from node modules to assets (so the browser can get hold of the file)
Added the code to draw the map in ngOnInit
import { Component, OnInit } from '#angular/core';
import { select } from 'd3-selection';
import { geomap } from 'd3-geomap';
#Component({
selector: 'app-test-draw',
templateUrl: './test-draw.component.html',
styleUrls: ['./test-draw.component.css']
})
export class TestDrawComponent implements OnInit {
constructor() { }
ngOnInit(): void {
const USAMap=geomap();
USAMap.geofile('/assets/USA.json');
USAMap.scale('1000');
USAMap.draw(select('#map'));
}
}
And added the CSS to the components CSS
#import "../../../node_modules/d3-geomap/dist/d3-geomap.css"
It seems happy, it does not complain about the code, and looking at the SVG object, there is data there and I can see state names. But it looks like it hasn't actually drawn it, or maybe drawn it 1px by 1px.
Any ideas

Related

HereMaps Javascript MemoryLeak?

I am currently trying to find some potential memory leaks in my angular application and found something which should regard to the hereMap, that I am using in one component.
This is the situation:
I have an Angular 12 SPA with two components:
ComponentA - completely empty angular component - just for routing away from component B
ComponentB - the component that is using the hereMap.
When switching routes from Component A to B to A I would expect the garbage collector to remove most of the allocated memory after going back to A after a certain amount of time or when clicking "Collect garbage" in DevTools.
Here is what drives me crazy:
Every time when I go to the route with ComponentB, it seems like mapsjs-core.js adds a new TileManager that stays in memory forever and holds an enormous amount of objects and Arrays (3.5k Arrays and 10k Objects each time) which adds up to like 3-5mb memory each time.
Heap Snapshot
Those objects include textures, meshes, shields, etc in multiple instances of TileManagers (TileManager_0, TileManager_1, TileManager_2 after 3 times of creating a new instance of ComponentB).
After ngOnDestroy of ComponentB got called, ComponentB is no longer part of the memory, so disposing the map seems to work as expected.
Here is how the components look like:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-component-a',
templateUrl: './component-a.component.html',
styleUrls: ['./component-a.component.styl']
})
export class ComponentAComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-component-b',
templateUrl: './component-b.component.html',
styleUrls: ['./component-b.component.styl']
})
export class ComponentBComponent implements OnInit, AfterViewInit, OnDestroy {
#ViewChild('map') public mapElement: ElementRef;
private map: H.Map;
private platform: H.service.Platform;
constructor() { }
ngOnInit(): void {
this.platform = new H.service.Platform({
apikey: myKey
});
}
ngAfterViewInit() {
const defaultLayers = this.platform.createDefaultLayers();
// Set min and max zoom level
defaultLayers.vector.normal.map.setMax(16);
defaultLayers.vector.normal.map.setMin(2);
// Initialize the map
this.map = new H.Map(
this.mapElement.nativeElement,
defaultLayers.vector.normal.map,
{
zoom: ZoomLevels.ZOOM_MIN_SINGLE_MAP
}
);
}
public ngOnDestroy(): void {
this.map.dispose();
}
}
We did performance testing on both simple vanilla Javascript application and angular application. And the object are getting released from the heap memory as required. Please find the below attached results.
And please keep note of the point that the Map is not supposed to dispose any other objects but itself, so application must explicitly dispose any other resource created by the application. The reason for it is very simple: let's take an application, that created a provider and populated it, the application might decide to re-use this provider, but dispose the map. And please follow below links for best practices.
1>Working of HERE Maps API for Javascript.
https://developer.here.com/documentation/maps/3.1.25.0/dev_guide/topics/get-started.html
2>Angular example.
https://developer.here.com/blog/display-here-maps-angular-web-application(please add disposing of heremap object in the ngOnDestroy method of angular i.e this.map.dispose())
3>Best Practices.
https://developer.here.com/documentation/maps/3.1.25.0/dev_guide/topics/best-practices.html

Angular 5 - Loading Script After View Is Loaded

I have a legacy script that I need to include in my angular application.
The thing about this script is that it relates to a specific component, and it has to be loaded only after the view of the component is loaded.
As for today, I succeeded to include it on OnInit function but sometimes (not always for some reason) the CLI throws an error about it.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-player-page',
templateUrl: './player-page.component.html',
styleUrls: ['./player-page.component.scss']
})
export class PlayerPageComponent implements OnInit {
public itemId: string;
constructor() {}
ngOnInit() {
//We loading the player srcript on after view is loaded
require('assets/scripts/player/player.js');
}
}
The script assumes that elements in the UI exists and it searches them by the id.
When adding it on top of the page, it doesn't work.
What is the best way to achieve the desired behavior?
There are multiple solutions to this issue.
declare the require const on top of your component
declare const require: any;
import { Component, OnInit } from '#angular/core';
#Component({})
...
use the dynamic import() function from typescript
ngAfterViewInit() {
//We loading the player script on after view is loaded
import('assets/scripts/player/player.js');
}
change the library to only start running after you call a function from the component, this way you can add it to the scripts array of your angular.json

Include pure javascript cesium overlay with angular cesium

I'm using angular 4+ and trying to integrate cesium. I have the globe working, and I'm trying to add overlays that have been built by another dev in pure js. I installed npm angular-cesium to get the map running, and i'm trying to include the js file to manipulate it (the angular-cesium devs said this was possible, but did not explain how)
I put the file globe.js in my component folder, and included it in the component like this:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
require('./globe.js')
declare var Cesium: any;
#Component({
selector: 'app-globe',
templateUrl: './globe.component.html',
styleUrls: ['./globe.component.css'],
encapsulation: ViewEncapsulation.None
})
export class GlobeComponent implements OnInit {
startup: any;
constructor() {
}
ngOnInit() {
startup()
}
}
it is clear that the globe.js file and function are imported and accessable in my component,
but i'm getting the error
globe.js
Module not found: Error: Can't resolve 'Cesium' in 'globe'
Somehow Cesium is not getting imported to globe. How do I fix this? is this the wrong way to integrate js with cesium angular?

Externally pass values to an Angular application

I'm thinking of separating our MVC-based website's front-end into few components and I was thinking about using Angular for that purpose (e.g. creating cart application that I can include to my view afterwards).
Problem is that I need to pass few variables into that application and I'm wondering how to do that safely and professionally. I was thinking about something like:
I'm going to ng build --prod --aot
I inject all my scripts to my view
I inject variables passed to view to my app
... and "code" representation for my thoughts:
Controller:
public function viewAction()
{
$this->view->addCss('angular/app/styles.css'); // adds styles for cart app
$this->view->addJS('angular/app/scripts.js'); // adds scripts for cart app
$this->view->setJSVariable('idCustomer', 555); // sets global var idCustomer
}
View:
<!-- bunch of HTML for view ... -->
<script>
// CartApp would be an angular app.
CartApp.use([
idCustomer
]);
</script>
So my question is... would it be possible (and would it be a good solution) to get the CartApp as an object and then make some function (like use in above example) that would set/pass the data? (let's say to some globally provided service/component or anything else). Or is there any other way to do this? Like taking the variables from inside the application from the window object? (as they're going to be bound to the window anyways). Thank you.
So I was going to suggest using input bindings... I've done that before in AngularJS but was surprised to find that using input bindings on the root component isn't supported yet. You can have fun reading this giant issue: https://github.com/angular/angular/issues/1858
The best post I saw there was Rob Wormald's which had a couple of suggestions:
to the initial question, if you really want to grab data from the
root, that's possible via
https://plnkr.co/edit/nOQuXE8hMkhakDNCNR9u?p=preview - note that it's not an input (because there's no angular context outside of it to do the input...) - its a simple string attribute
which you'd need to parse yourself.
ideally though, you'd do as
#robtown suggested and actually pass the data in javascript, rather
than passing it as a DOM string and retrieving / parsing it yourself
(which has never really been a supported case in angular, despite the
explicit-warned-against usage of ng-init in angular1 to accomplish
this) - see https://plnkr.co/edit/PoSd07IBvYm1EzeA2yJR?p=preview for a
simple, testable example of how to do this.
So the 2 good options I saw were:
Add normal HTML attributes to the root component:
<app-root appData='important stuff'></app-root>
and use ElementRef to fetch them:
#Component({
selector: 'app-root'
})
export class AppComponent {
constructor(el: ElementRef) {
console.log(el.nativeElement.getAttribute('appData'));
}
}
Would probably work best if you are just dealing with strings or config flags. If you are passing JSON, you will need to manually parse it.
Have the server render the data as JavaScript and import it in your app:
Have the server render something like this to a script tag or JS file that is loaded before Angular is bootstrapped:
window.APP_DATA = { ids: [1, 2, 3] }
Tell your NgModule about it using a provider:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
#NgModule({
providers: [{ provide: 'AppData', useValue: (<any> window).APP_DATA }],
bootstrap: [AppComponent]
})
export class AppModule { }
And then use it as a normal Angular service:
import {Component, Inject} from '#angular/core';
#Component({
selector: 'app-root'
})
export class AppComponent {
constructor(#Inject('AppData') appData) {
console.log(appData.ids);
}
}

In NativeScript with Angular, how can I get an interface element the id?

I'm trying to avoid unexpected behavior in a NativeScript application.
When I walk into any screen that has a search field (SearchBar) system puts the focus on the search field automatically.
Then I got help with a friend who gave me a function to solve this problem. But the problem I'm facing right now is that I can not get the interface element. I have the following code:
import {Component} from "#angular/core";
import {Page} from "ui/page";
import frame = require("ui/frame");
#Component({
selector: "clientes",
templateUrl: "pages/clientes/clientes.html",
styleUrls: ["pages/clientes/clientes.common.css",
"pages/clientes/clientes.css"]
})
export class ClientesPage {
page;
constructor(private _router: Router) {
this.page = <Page>frame.topmost().currentPage;
this.removeSearchFocus();
}
removeSearchFocus() {
var parent = this.page.getViewById('box-lista');
var searchBar = this.page.getViewById('barra-busca');
console.log(JSON.stringify(parent)); // UNDEFINED
console.log(JSON.stringify(searchBar)); // UNDEFINED
if (parent.android) {
parent.android.setFocusableInTouchMode(true);
parent.android.setFocusable(true);
searchBar.android.clearFocus();
}
}
}
And then I have the template:
<Page.actionBar>
<ActionBar title="Clientes"></ActionBar>
</Page.actionBar>
<StackLayout orientation="vertical" id="box-lista">
<SearchBar hint="Buscar" cssClass="mh mv" id="barra-busca"></SearchBar>
</StackLayout>
My goal is to get the auto focus of the search field, but I can not get the interface element.
to expand on Nikolay's answer above this to use #ViewChild add #foo to your stack layout so it looks like:
<StackLayout #foo ....
then:
#ViewChild('foo') stackLayout: ElementRef;
ngAfterViewInit() {
let yourStackLayoutInstance = this.stackLayout.nativeElement;
}
it won't get the element by ID but this is the more ng2 way of interacting with elements and keeps your code more loosely coupled to the DOM.
To get element in your project you should use #ViewChild decorator, which will help you to create new property that point at the SearchBar. In regard to that you could review my project here and also to review this article: Building Apps with NativeScript and Angular 2

Categories