I am writing code to upload an image file. I need to know the dimensions(height and width) of the image that will be uploaded before I call the function to upload.
Is there a way in angular 2 by which I can extract the image dimension? If so, how?
I created function to get image size
getImgSize(imageSrc: string): Observable<ISize> {
let mapLoadedImage = (event): ISize => {
return {
width: event.target.width,
height: event.target.height
};
}
var image = new Image();
let $loadedImg = fromEvent(image, "load").pipe(take(1), map(mapLoadedImage));
// Rxjs 4 - let $loadedImg = Observable.fromEvent(image, "load").take(1).map(mapLoadedImage);
image.src = imageSrc;
return $loadedImg;
}
interface ISize { width: number; height: number; }
Also you can subscribe on load event in html
<img (load)="loadedImg($event)" [src]="imageSrc"> and get size from it.
With the Angular2 approach, I'll create a custom directive to get the height and width of any element. For img, I'll apply it(directive) in the img tag and whenever I want to get the height & width of an img, I just need click it. You can modify according to your need.
DEMO : https://plnkr.co/edit/3tibSEJCF734KQ3PBNZc?p=preview
directive.ts
import { Directive,Input,Output,ElementRef,Renderer} from '#angular/core';
#Directive({
selector:"[getHeightWidth]",
host:{
'(click)':"show()"
}
})
export class GetEleDirective{
constructor(private el:ElementRef){
}
show(){
console.log(this.el.nativeElement);
console.log('height---' + this.el.nativeElement.offsetHeight);
console.log('width---' + this.el.nativeElement.offsetWidth);
}
}
app.ts
#Component({
selector: 'my-app',
template: `
<div style="width:200px;height:300px">
<img getHeightWidth <!-- here I'm using getHeightWidth directive-->
[src]="source" alt="Angular2"
width="100%"
height="100%">
</div>
`,
})
export class AppComponent {
source='images/angular.png';
}
Simply you can use following code to get the width and height (Resolution) of the Image.
HTML Code
<img #pic [src]="imgURL" (load)="onLoad()>
In Angular
#ViewChild('pic', { static: false }) pic: ElementRef;
onLoad() {
console.log((this.pic.nativeElement as HTMLImageElement).naturalWidth);
console.log((this.pic.nativeElement as HTMLImageElement).naturalHeight);
}
In case if you need to get the image size in ts file:
getImageDimension(image): Observable<any> {
return new Observable(observer => {
const img = new Image();
img.onload = function (event) {
const loadedImage: any = event.currentTarget;
image.width = loadedImage.width;
image.height = loadedImage.height;
observer.next(image);
observer.complete();
}
img.src = image.url;
});
}
Call above method:
const image = {
url: 'https://kalgudi.com/store/assets/images/e-mahila1.jpg',
context: 'Mahila self help group'
}
this.getImageDimension(image).subscribe(
response => {
console.log(response);
}
);
In component.ts
this.uploadService.validateandUploadFile(files, 300, 300);
In service.ts file
import { Injectable } from '#angular/core';
import * as AWS from 'aws-sdk/global';
import * as S3 from 'aws-sdk/clients/s3';
import { BehaviorSubject } from 'rxjs';
FOLDER = '/';
imageUrl = "";
resData: BehaviorSubject<any> = new BehaviorSubject(null);
data = { message: "", data: "" };
constructor() { }
validateandUploadFile(file, Iheight, Iwidth) {
let fileToUpload = file;
if (fileToUpload.type == "image/jpeg" || fileToUpload.type == "image/png" || fileToUpload.type == "image/jpeg") {
//Show image preview
let reader = new FileReader();
reader.onload = (event: any) => {
var img = new Image();
img.onload = () => {
let width = img.width;
let height = img.height;
if (width <= Iwidth && height <= Iheight) {
this.imageUrl = event.target.result;
this.uploadfile(file);
} else {
this.data.message = "You can maximum upload " + Iheight + " * " + Iwidth + " File";
this.data.data = "";
this.resData.next(this.data);
return this.resData;
}
};
img.src = event.target.result;
}
reader.readAsDataURL(fileToUpload);
} else {
this.data.message = "You can't be able to upload file except JPG and PNG format";
this.data.data = "";
this.resData.next(this.data);
return this.resData;
}
}
uploadfile(file) {
if (file != null) {
const bucket = new S3(
{
accessKeyId: '***********************',
secretAccessKey: '**********************************',
region: 'us-east-2'
}
);
const params = {
Bucket: '*********',
Key: file.name,
Body: file,
ACL: 'public-read'
};
var that = this;
bucket.upload(params, function (err, data) {
if (err) {
console.log('There was an error uploading your file: ', err);
return false;
}
console.log('Successfully uploaded file.', data);
that.data.message = "Successfully uploaded file.";
that.data.data = data.Location;
that.resData.next(that.data);
return that.resData;
});
}
}
You have to use JS code to find the height and width of image as follow :
<!DOCTYPE html>
<head>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
function readURL(input)
{
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#image1')
.attr('src', e.target.result);
};
reader.readAsDataURL(input.files[0]);
}
}
function upload()
{
var img = document.getElementById('image1');
var width = img.clientWidth;
var height = img.clientHeight;
alert(width + " : " + height);
//check height and width using above two variables (width/height) in if block and place upload code in if block...
}
</script>
</head>
<body>
<input type='file' onchange="readURL(this);" /><input type="button" value="Upload" onClick="upload()" /><br>
<img id="image1" src="#" alt="your image" height="auto" width="auto" />
</body>
</html>
In above code we have to place selected image in image element, after during upload process check height and width and process to upload.
Thanks...
Related
Calendar don't show events in first load.
Only when I trigger any event like click in button to change the view from month to week for example.
I'm using Angular Calendar 6 + LINK
My project is made in Angular 6.
The calendar don't show data in first load, only after it when I trigger any event.
Everything is working as expected, so Ill give the Html (part of them) and the most important: the most important in this case.
HTML
...
<div [ngSwitch]="view">
<mwl-calendar-month-view *ngSwitchCase="CalendarView.Month" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" [activeDayIsOpen]="activeDayIsOpen" (dayClicked)="dayClicked($event.day)"
(eventClicked)="handleEvent('Clicked', $event.event)" (eventTimesChanged)="eventTimesChanged($event)"
[locale]="locale" [weekStartsOn]="weekStartsOn" [weekendDays]="weekendDays" >
</mwl-calendar-month-view>
<mwl-calendar-week-view *ngSwitchCase="CalendarView.Week" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)" [locale]="locale" [weekStartsOn]="weekStartsOn"
[weekendDays]="weekendDays" (beforeViewRender)="beforeMonthViewRender($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view *ngSwitchCase="CalendarView.Day" [viewDate]="viewDate" [events]="events"
[refresh]="refresh" (eventClicked)="handleEvent('Clicked', $event.event)"
(eventTimesChanged)="eventTimesChanged($event)" [locale]="locale" >
</mwl-calendar-day-view>
</div>
...
TYPESCRIPT
...
import {
Component,
ChangeDetectionStrategy,
ViewChild,
TemplateRef,
OnInit
} from "#angular/core";
import {
CalendarEvent,
CalendarView,
DAYS_OF_WEEK,
CalendarEventAction,
CalendarEventTimesChangedEvent,
CalendarDateFormatter
} from "angular-calendar";
import { NgbModal } from "#ng-bootstrap/ng-bootstrap";
import { Subject, Observable } from "rxjs";
import {
isSameDay,
isSameMonth} from "date-fns";
import { HomeService } from "../shared/service/home.service";
import { CalendarioEvento } from "../shared/model/calendario-eventos.model";
import { FormGroup, FormBuilder } from "#angular/forms";
import { CustomDateFormatter } from "../shared/service/custom-date-formatter.provide";
#Component({
selector: "mwl-demo-component",
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: "./home.component.html",
styleUrls: ["./home.component.css"],
providers: [
HomeService,
{
provide: CalendarDateFormatter,
useClass: CustomDateFormatter
}
]
})
export class HomeComponent implements OnInit {
#ViewChild("modalContent") modalContent: TemplateRef<any>;
activeDayIsOpen: boolean = true;
view: CalendarView = CalendarView.Month;
viewDate: Date = new Date();
locale: string = "pt-PT";
modalData: {
action: string;
event: CalendarEvent;
};
weekStartsOn: number = DAYS_OF_WEEK.MONDAY;
weekendDays: number[] = [DAYS_OF_WEEK.FRIDAY, DAYS_OF_WEEK.SATURDAY];
CalendarView = CalendarView;
listEvents: CalendarioEvento[] = [];
evento: CalendarioEvento;
eventsJson: string;
filtroForm: FormGroup;
botao: string;
acaoPermitida: boolean;
events: CalendarEvent[] = [];
events$: Observable<Array<CalendarEvent<{ event: any }>>>;
constructor(
private modal: NgbModal,
private homeService: HomeService ) {}
actions: CalendarEventAction[] = [
{
label: '<i class="fa fa-fw fa-pencil"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.handleEvent("Edited", event);
}
},
{
label: '<i class="fa fa-fw fa-times"></i>',
onClick: ({ event }: { event: CalendarEvent }): void => {
this.events = this.events.filter(iEvent => iEvent !== event);
this.handleEvent("Deleted", event);
}
}
];
refresh: Subject<any> = new Subject();
ngOnInit() {
this.myMethodToInit();
}
myMethodToInit() {
this.homeService.getAll().subscribe(data => {
this.listEvents = data;
this.listEvents.forEach(element => {
var diaInicial = element.dataInicio.toString().substring(0, 2);
var diaFim = element.dataFim.toString().substring(0, 2);
var mesInicial = element.dataInicio.toString().substring(3, 5);
var mesFim = element.dataFim.toString().substring(3, 5);
var anoInicial = element.dataInicio.toString().substring(6, 10);
var anoFim = element.dataFim.toString().substring(6, 10);
var dataInicio = anoInicial + "-" + mesInicial + "-" + diaInicial;
var dataFim = anoFim + "-" + mesFim + "-" + diaFim;
let eve: CalendarEvent = {
title: element.descricao,
start: new Date(dataInicio),
id: element.id,
end: new Date(dataFim),
actions: this.actions
};
this.events.push(eve);
});
});
}
dayClicked({ date, events }: { date: Date; events: CalendarEvent[] }): void {
if (isSameMonth(date, this.viewDate)) {
this.viewDate = date;
if (
(isSameDay(this.viewDate, date) && this.activeDayIsOpen === true) ||
events.length === 0
) {
this.activeDayIsOpen = false;
} else {
this.activeDayIsOpen = true;
}
}
}
eventTimesChanged({
event,
newStart,
newEnd
}: CalendarEventTimesChangedEvent): void {
this.events = this.events.map(iEvent => {
if (iEvent === event) {
return {
...event,
start: newStart,
end: newEnd
};
}
return iEvent;
});
this.handleEvent("Dropped or resized", event);
}
handleEvent(action: string, event: CalendarEvent): void {
event.start = event.start;
this.modalData = { event, action };
this.modal.open(this.modalContent, { size: "lg" });
document
.getElementsByTagName("ngb-modal-window")
.item(0)
.setAttribute("id", "modal");
document.getElementById("modal").style.opacity = "1";
}
addEvent(action: string, event: CalendarEvent): void {
this.modalData = { event, action };
this.modal.open(this.modalContent, { size: "lg" });
document
.getElementsByTagName("ngb-modal-window")
.item(0)
.setAttribute("id", "modal");
document.getElementById("modal").style.opacity = "1";
}
deleteEvent(eventToDelete: CalendarEvent) {
this.events = this.events.filter(event => event !== eventToDelete);
}
setView(view: CalendarView) {
this.view = view;
}
closeOpenMonthViewDay() {
this.activeDayIsOpen = false;
}
resetar() {}
salvar() {
console.log("titulo:" + this.modalData.event.title);
console.log("start:" + this.modalData.event.start);
console.log("end:" + this.modalData.event.end);
}
}
...
FIRST LOAD:
AFTER ANY EVENT (for example, click in week view and return to month view):
Problem solved after some hours rs.
Only 2 adjusts in component.ts and it works!
1º Include: encapsulation: ViewEncapsulation.None
2º Inside ngOnInit, after retrieve all events refresh the calendar with the command: this.refresh.next();
Code:
ViewEncapsulation.None
...
selector: "calendario-ico",
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: "./calendario.component.html",
styleUrls: ["./calendario.component.css"],
encapsulation: ViewEncapsulation.None,
providers: [
...
this.refresh.next();
ngOnInit() {
this.AtivarFormulario();
this.homeService.getAll("", "", "").subscribe(data => {
this.listEvents = data;
this.listEvents.forEach(element => {
var diaInicial = element.dataInicio.toString().substring(0, 2);
var diaFim = element.dataFim.toString().substring(0, 2);
var mesInicial = element.dataInicio.toString().substring(3, 5);
var mesFim = element.dataFim.toString().substring(3, 5);
var anoInicial = element.dataInicio.toString().substring(6, 10);
var anoFim = element.dataFim.toString().substring(6, 10);
var dataInicio = anoInicial + "-" + mesInicial + "-" + diaInicial;
var dataFim = anoFim + "-" + mesFim + "-" + diaFim;
let eve: CalendarEvent = {
title: element.descricao,
start: new Date(dataInicio),
id: element.id,
end: new Date(dataFim),
actions: this.actions
};
this.events.push(eve);
});
this.refresh.next();
});
}
Hi my suggestion is remove ngOnInit, it will load first time.
I have create a page with different input elements with file upload. While saving the form with multiple files along with form input elements using angular 6, the file object is empty {} in console an http service in network tab.
Here is my code:
onFileChanged(event) {
this.selectedFiles = event.target.files;
const uploadData = new FormData();
uploadData.append('myFile', this.selectedFiles, this.selectedFiles[0].name);
this.createFormData.attachment = uploadData;
};
Can anyone provide a sample to help me?
This is example of upload method in service. Pass files and input values from component to service, create formData, loop on files and append each file to formData and same with input values.
upload(files, inputs) {
let formData = new FormData();
files.map((file) => {
formData.append('file', file);
});
inputs.map((input) => {
formData.append(`${input.name}`, input.value);
});
return this.http.post(`some/api/upload`, formData)
.pipe(map((res) => res.data));
}
With this example your request should contain all array of files and inputs, also if you need some special header add it after formData in post method (in my case i handle headers in interceptor).
upload(files, inputs) {
let formData = new FormData();
files.map((file) => {
formData.append('file', file);
});
inputs.map((input) => {
formData.append(`${input.name}`, input.value);
});
const headers = new HttpHeaders({
'Accept': 'application/json'
});
const options = { headers: headers };
return this.http.post(`some/api/upload`, formData, options)
.pipe(map((res) => res.data));
}
Have a look at following example with npm's ng2-file-upload (in this case with a fontAwsome-icon).
myFileUploadForm.component.html:
<!-- file upload -->
<input #fileToUpload type="file" style="display:none;" ng2FileSelect (change)="onFileChanged($event)" [uploader]="uploader"
name="customerFile" id="customerFile" class="customerFile" />
<a href="javascript:document.getElementById('customerFile').click();">
<fa class="ql-upload" *ngIf="buttonDeaktivieren" [title]="'Upload'" [name]="'upload'" [size]="0.9" [border]=false></fa>
</a>
myFileUploadForm.component.ts (I'll ommit the obvious parts with ..):
import { Component, OnInit, ViewChild, OnDestroy, TemplateRef } from '#angular/core';
import { Subscription, Observable } from '../../../../../node_modules/rxjs';
....
...
import { FileUploader } from 'ng2-file-upload';
#Component({
....
...
..
})
export class myFileUploadFormComponent implements OnInit, OnDestroy {
public uploader: FileUploader = new FileUploader({ url:
'http://localhost:3000/files/uploadFile/', itemAlias: 'customerFile' });
filesArray = [];
constructor(
...
..
private http: Http
) { }
ngOnInit() {
....
...
..
}
// INFO: Function for opening confirm modal when deleting file
openDeleteFileModal(file) {
const initialState = {
file: file
};
this.deleteFileModalRef = this.modalService.show(ConfirmFileDeletionComponent, {
initialState, class: 'modal-md modal-dialog-centered' });
}
// INFO: Handy helper for assigning appropriate file-icon according to extension
getFilesIcon(file) {
if (file === 'docx') {
return 'file-word-o';
} else if (file === 'jpeg' || file === 'jpg' || file === 'png') {
return 'image';
} else if (file === 'pdf') {
return 'file-pdf-o';
} else if (file === 'xlsx' || file === 'xls') {
return 'file-excel-o';
} else if (file === 'pptx') {
return 'file-powerpoint-o';
} else if (file === 'zip') {
return 'file-archive-o';
} else {
return 'file';
}
}
/* INFO : service for downloading the uploaded file */
downloadFile(filename) {
this.fileDataService.downloadFile(filename);
}
onFileChanged(event) {
this.uploader.onBuildItemForm = (fileItem: any, form: any) => {
form.append('id', this.customer.id);
form.append('customername', this.customer.customername);
};
this.uploader.uploadAll();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Obviously this is my implementation suited to my needs (uploaded files are related to a certain customer and are displayed with icons according to extension and listed for future download or deletion as well), but I'm sure you can get it done by adjusting the code to your needs and write the relevant services. Have a nice one weekend ;)
I am trying to upload a photo in my React application, along with some form data. It works with uploading form data from ItemAdd.jsx, a child component of ItemList.jsx. However, when I try to also POST an image file with this data, the image property is undefined when it hits the server.
My suspicion is that I'm using the wrong content-type in the request, but I'm not sure what I should be using instead (if that is the issue here).
Parent Component - ItemList.jsx
import React from 'react';
import 'whatwg-fetch';
import classNames from 'classnames';
import ItemAdd from './ItemAdd.jsx';
export default class ItemList extends React.Component {
constructor() {
super();
this.createItem = this.createItem.bind(this);
}
createItem(newItem) {
console.log('PHOTO:', newItem.image);
fetch('/api/item', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newItem),
}).then(response => {
}).catch(err => {
});
}
render() {
return (
<div>
<ItemAdd createItem={this.createItem} />
</div>
);
}
}
Child Component - ItemAdd.jsx
import React from 'react';
export default class ItemAdd extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
image: null,
imagePreviewUrl: null
}
}
handleSubmit(e) {
e.preventDefault();
let form = document.forms.itemAdd;
this.props.createItem({
name: form.name.value,
image: this.state.image
});
// Clear the form and state for the next input.
form.name.value = "";
this.state.image = null;
this.state.imagePreviewUrl = null;
}
handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
image: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
render() {
let { imagePreviewUrl } = this.state;
let $imagePreview = null;
if (imagePreviewUrl) {
$imagePreview = (<img src={imagePreviewUrl} className={'img-preview'} />);
} else {
$imagePreview = (<div className="previewText">Please select an image.</div>);
}
return (
<div>
<form name="itemAdd" onSubmit={this.handleSubmit}>
<table>
<tr>
<td><label for="name">Name:</label></td>
<td><input type="text" name="name" id="name" placeholder="Name" /></td>
</tr>
<tr>
<td><input type="file" onChange={(e) => this.handleImageChange(e)} /></td>
<td>
<div className="img-preview">
{$imagePreview}
</div>
</td>
</tr>
<tr>
<td><button>Add</button></td>
</tr>
</table>
</form>
</div>
);
}
}
You might not be able to post an image as part of JSON data, calling JSON.stringify() on an image is not a good idea.
I would recommend using formData to submit the form, which makes it multipart/form-data content type.
You might have to handle that differently in the backend.
Example :
createItem(newItem) {
console.log('PHOTO:', newItem.image);
const h = {}; //headers
let data = new FormData();
data.append('image', newItem.image);
data.append('name', newItem.name);
h.Accept = 'application/json'; //if you expect JSON response
fetch('/api/item', {
method: 'POST',
headers: h,
body: data
}).then(response => {
// TODO : Do something
}).catch(err => {
// TODO : Do something
});
}
You can read more on formData
I'm following this tutorial to write a Google Maps React Component that lazy loads the library. I have implemented ScriptCache from this gist. The problem I have is that the code only shows Loading map... and the map is never rendered. Any obvious thing that I have missed?
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<title>App</title>
</head>
<body>
<div id="app"></div>
<script src="/Scripts/dist/bundle.js"></script>
</body>
</html>
index.tsx:
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactRouter from "react-router";
import * as ReactBootstrap from "react-bootstrap";
import Container from "./Google/GoogleMapComponent";
ReactDOM.render(
<div>
<Container google={(window as any).google} />
</div>,
document.getElementById("app")
);
GoogleApi.tsx:
export const GoogleApi = function (opts: any) {
opts = opts || {}
const apiKey: any = opts.apiKey;
const libraries: any = opts.libraries || [];
const client: any = opts.client;
const URL: string = 'https://maps.googleapis.com/maps/api/js';
const googleVersion: string = '3.22';
let script: any = null;
let google: any = (window as any).google = null;
let loading = false;
let channel: any = null;
let language: any = null;
let region: any = null;
let onLoadEvents: any[] = [];
const url = () => {
let url = URL;
let params = {
key: apiKey,
callback: 'CALLBACK_NAME',
libraries: libraries.join(','),
client: client,
v: googleVersion,
channel: channel,
language: language,
region: region
}
let paramStr = Object.keys(params)
.filter(k => !!(params as any)[k])
.map(k => `${k}=${(params as any)[k]}`).join('&');
return `${url}?${paramStr}`;
}
return url();
}
export default GoogleApi
GoogleApiComponent.tsx:
import * as React from "react";
import * as ReactDOM from 'react-dom'
import cache from './ScriptCache'
import GoogleApi from './GoogleApi'
const defaultMapConfig = {}
export const wrapper = (options: any) => (WrappedComponent: any) => {
const apiKey = options.apiKey;
const libraries = options.libraries || ['places'];
class Wrapper extends React.Component<any, any> {
constructor(props: any, context: any) {
super(props, context);
this.state = {
loaded: false,
map: null,
google: null
}
}
scriptCache: any;
map: any;
mapComponent: any
refs: {
[string: string]: any;
map: any;
}
componentDidMount() {
const refs: any = this.refs;
this.scriptCache.google.onLoad((err: any, tag: any) => {
const maps = (window as any).google.maps;
const props = Object.assign({}, this.props, {
loaded: this.state.loaded
});
const mapRef: any = refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let center = new maps.LatLng(this.props.lat, this.props.lng)
let mapConfig = Object.assign({}, defaultMapConfig, {
center, zoom: this.props.zoom
})
this.map = new maps.Map(node, mapConfig);
this.setState({
loaded: true,
map: this.map,
google: (window as any).google
})
});
}
componentWillMount() {
this.scriptCache = cache({
google: GoogleApi({
apiKey: apiKey,
libraries: libraries
})
});
}
render() {
const props = Object.assign({}, this.props, {
loaded: this.state.loaded,
map: this.state.map,
google: this.state.google,
mapComponent: this.refs.map
})
return (
<div>
<WrappedComponent {...props} />
<div ref='map' />
</div>
)
}
}
return Wrapper;
}
export default wrapper;
GoogleMapComponent.tsx:
import * as React from "react";
import * as ReactDOM from 'react-dom'
import GoogleApiComponent from "./GoogleApiComponent";
export class Container extends React.Component<any, any> {
render() {
const style = {
width: '100px',
height: '100px'
}
return (
<div style={style}>
<Map google={this.props.google} />
</div>
)
}
}
export default GoogleApiComponent({
apiKey: 'AIzaSyAyesbQMyKVVbBgKVi2g6VX7mop2z96jBo ' //From Fullstackreact.com
})(Container)
export class Map extends React.Component<any, any> {
refs: {
[string: string]: any;
map: any;
}
map: any;
componentDidMount() {
this.loadMap();
}
componentDidUpdate(prevProps: any, prevState: any) {
if (prevProps.google !== this.props.google) {
this.loadMap();
}
}
loadMap() {
if (this.props && this.props.google) {
// google is available
const {google} = this.props;
const maps = google.maps;
const mapRef = this.refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let zoom = 14;
let lat = 37.774929;
let lng = -122.419416;
const center = new maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center: center,
zoom: zoom
})
this.map = new maps.Map(node, mapConfig);
}
// ...
}
render() {
return (
<div ref='map'>
Loading map...
</div>
)
}
}
ScriptCache.tsx:
let counter = 0;
let scriptMap = new Map();
export const ScriptCache = (function (global: any) {
return function ScriptCache(scripts: any) {
const Cache: any = {}
Cache._onLoad = function (key: any) {
return (cb: any) => {
let stored = scriptMap.get(key);
if (stored) {
stored.promise.then(() => {
stored.error ? cb(stored.error) : cb(null, stored)
})
} else {
// TODO:
}
}
}
Cache._scriptTag = (key: any, src: any) => {
if (!scriptMap.has(key)) {
let tag : any = document.createElement('script');
let promise = new Promise((resolve: any, reject: any) => {
let resolved = false,
errored = false,
body = document.getElementsByTagName('body')[0];
tag.type = 'text/javascript';
tag.async = false; // Load in order
const cbName = `loaderCB${counter++}${Date.now()}`;
let cb: any;
let handleResult = (state: any) => {
return (evt: any) => {
let stored = scriptMap.get(key);
if (state === 'loaded') {
stored.resolved = true;
resolve(src);
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = []
} else if (state === 'error') {
stored.errored = true;
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = [];
reject(evt)
}
cleanup();
}
}
const cleanup = () => {
if (global[cbName] && typeof global[cbName] === 'function') {
global[cbName] = null;
}
}
tag.onload = handleResult('loaded');
tag.onerror = handleResult('error')
tag.onreadystatechange = () => {
handleResult(tag.readyState)
}
// Pick off callback, if there is one
if (src.match(/callback=CALLBACK_NAME/)) {
src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`)
cb = (window as any)[cbName] = tag.onload;
} else {
tag.addEventListener('load', tag.onload)
}
tag.addEventListener('error', tag.onerror);
tag.src = src;
body.appendChild(tag);
return tag;
});
let initialState = {
loaded: false,
error: false,
promise: promise,
tag
}
scriptMap.set(key, initialState);
}
return scriptMap.get(key);
}
Object.keys(scripts).forEach(function (key) {
const script = scripts[key];
Cache[key] = {
tag: Cache._scriptTag(key, script),
onLoad: Cache._onLoad(key)
}
})
return Cache;
}
})(window)
export default ScriptCache;
Even though Container looks like this nothing will get printed:
export class Container extends React.Component<any, any> {
render()
{
const style = {
width: '100px',
height: '100px'
}
return (
<div style={style}>
<Map google={this.props.google} />
</div>
)
}
}
If I do however set width and height on map ref directly it will print correctly. You can not add width and height in percent.
export class Map extends React.Component<any, any> {
...
render() {
const style = {
width: '100vw',
height: '100vh'
}
return (
<div ref='map' style={style}>
Loading map...
</div>
)
}
}
I have written a directive by using typescript. here is my directive below.
'use strict';
module App.Directives {
interface IPageModal extends ng.IDirective {
}
interface IPageModalScope extends ng.IScope {
}
class PageModal implements IPageModal {
static directiveId: string = 'pageModal';
restrict: string = "A";
constructor(private $parse: ng.IParseService) {
}
link = (scope: IPageModalScope, element, attrs) => {
element.click((event) => {
event.preventDefault();
var options = {
backdrop: 'static',
keyboard: false
};
event.openModal = function () {
$('#' + attrs['targetModal']).modal(options);
};
event.showModal = function () {
$('#' + attrs['targetModal']).modal('show');
};
event.closeModal = function () {
$('#' + attrs['targetModal']).modal('hide');
};
var fn = this.$parse(attrs['pageModal']);
fn(scope, { $event: event });
});
}
}
//References angular app
app.directive(PageModal.directiveId, ['$parse', $parse => new PageModal($parse)]);
}
Use in HTML
<button class="btn blue-grey-900" target-modal="emplpyeeViewModal" page-modal="vm.addEmployee($event)">
<i class="icon-plus m-b-xs"></i>
</button>
Use in Controller
addEmployee($event) {
$event.openModal();
};
This line does not work. var fn = this.$parse(attrs['pageModal']); . I can not understand what is wrong. The error is
this.$parse is undefined.
and Service is called two times
It's quite trivial: your this is not your class'es scope because the function openmodal(event) { defines its own.
Declare the function on a class level or use arrow function instead, e.g.
link = (scope: IPageModalScope, element, attrs) => {
element.click((event) => {
event.preventDefault();
var options = {
backdrop: 'static',
keyboard: false
};
event.openModal = function () {
$('#' + attrs['targetModal']).modal(options);
};
event.showModal = function () {
$('#' + attrs['targetModal']).modal('show');
};
event.closeModal = function () {
$('#' + attrs['targetModal']).modal('hide');
};
var fn = this.$parse(attrs['pageModal']);//does not work here
fn(scope, { $event: event });
});
}