i am trying to bind the details of an object in a component but i am unable to capture the object? - angular2-template

in the component if i try to do console.log(patient) is see a xml represented object is coming into my console but if i try to bind the data in my component from the returned object i am unable to for suppose in my component in the template section i am doing {{patient.Patient_Name}} but i am unable to see the data in the component?
#Component({
selector: 'dashboard-thumbnail',
template: <h2>Name:{{patients.Patient_Name}}</h2>
})
export class DashboardThumbnailComponent {
patients : Patient
constructor(private dashboardService : DashboardService, private activatedRoute: ActivatedRoute){}
ngOnInit(){
this.dashboardService.getPatient(+this.activatedRoute.snapshot.params['id']).subscribe((patient) => {
this.patients.Patient_Name = patient.Patient_Name;enter code here
console.log(patient);
})
}
}

I understand after lot of research that the return type of my getPatient method is an array so to bind the data i had to loop through the patients object instead of directly binding it. hope this helps!!
enter code here<div *ngFor = "let patient of patients" class = "well hoverwell thumbnail" >
<h2>Name : {{patient.Patient_Name}}</h2>

Related

Passing data to an ancestor component vue3

I have a multi-step form in Vue3 that has the following structure:
<Formroot />
...
<formPage2 />
...
<postcodeWidget />
I have a data structure in my formRoot that holds all the field values from the child components and then uses them to make an external API call and present a result.
I use Props to pass the data down to the child components and then emit from the children to the parent.
The issue is, my autocomplete widget - which pulls from an external api - does all the autocomplete in the setup() function. I cannot figure out the best way to communicate input from that widget back up to the formRoot component.
I tried emitting from the widget but I can't access the instance from within setup, and I can't seem to access the data from setup variables within an instance method.
For example, I have a function called changePostcode that fires on input to the field:
methods: {
changePostcode(e){
//I have tried calling the input event:
this.$emit('update:postcode', e.target.value)
//I have tried accessing my setup variable:
this.$emit('update:postcode', this.selectedPostcode) //or postcode.value this is the actull value I want to emit.
//these dont work.They return nothing.
},
}
my selectedPostcode variable is set in the setup() function as follows:
setup() {
...
let selectedPostcode = ref('')
let searchTerm = ref('')
...
// searchTerm is used in a filter with data from an external API to offer suggestions. This is the ultimate source of the "location" object
const selectPostcode = (location) => {
selectedPostcode.value = location.postcode
searchTerm.value = location.locality
}
return {
searchTerm,
...
selectPostcode,
selectedPostcode,
...
}
}
I have a locality and a postcode variable because I want to populate the input with a "locality" that includes the full name of the suburb while I want to emit only the post/zip code.
My setup does a bunch of other work including calling and api for a list of suburb and filtering on user input to make suggestions. That all works fine.
In summary,
A multi step form
One step includes a nested component that needs to pass data up to the root ancestor
I cannot seem to access/emit data from setup() back up to the ancestor element
What is the right way to do this? It seems like it should be a pretty common use case.
I looked into provide/inject as well but I also couldn't understand how to send data back up to the ancestor only down to the child.
The ancestor could provide a function (e.g., a setter) that the nested component could inject to communicate a value back to the ancestor:
// RootAncestor.vue
<script setup>
import { ref, provide } from 'vue'
const postCode = ref('initial value')
const setPostCode = value => {
postCode.value = value
}
provide('setPostCode', setPostCode)
</script>
// NestedComponent.vue
<script setup>
import { inject } from 'vue'
const setPostCode = inject('setPostCode')
const save = () => {
setPostCode('12345')
}
</script>
demo

cannot bind event to object method

I have a vue single file component, which have a custom class instance as property: now i want to bind a event to a method of this class instance but i'm having issues, there is a simplified version of my code files
VueJS single file component
<template>
<div #click="class_prop.method"></div>
</template>
export default {
data() {
return {
class_prop: new CustomClass(),
};
},
}
CustomClass
class CustomClass {
constructor(){
this.prop = 'default_value';
}
method(){
this.prop = 'new_value';
console.log(this.prop);
}
}
The error
When clicking on the page element i receive this error:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'prop' of null"
But when i try to call the custom class method from the browser console (i'm using Chrome with Vue Devtools extension) i get NO error, it works correctly:
$vm0.class_prop.method()
Since i get two different behaviours of my code i cannot tell if it is my class wrong, the vue single file component, or something else.
Behind the scenes Vue somehow called or applied your method using null. Thus the error you've mentioned:
Error in v-on handler: "TypeError: Cannot read property 'prop' of null"
What can you do to workaround the problem?
You can use a lambda expression, so you can have full control of the object owner of the method, like this:
<div #click="() => class_prop.method()"></div>
You can also use the class instance in a method like this:
export default {
data: () => ({
foo: new CustomClass(),
}),
methods: {
bar() {
this.foo.method();
},
},
}
And then in your template:
<div #click="bar">{{ foo.prop }}</div>
What you see is not Vue's fault, it's just plain JavaScript.
Here is a citation from great JS learning resource
The consequences of unbound this
If you come from another programming language, then you are probably used to the idea of a "bound this", where methods defined in an object always have this referencing that object.
In JavaScript this is “free”, its value is evaluated at call-time and does not depend on where the method was declared, but rather on what object is “before the dot”.
Here is very simple example of consequences of above paragraphs (and why your code does not work):
class CustomClass {
constructor() {
this.prop = 'default_value';
}
method() {
this.prop = 'new_value';
console.log(this.prop);
}
}
let instance = new CustomClass()
instance.method() // this works OK
let f = instance.method
f() // this does not! f is "unbound" ....have no "this"

How to loop over an array of objects and display it in UI using angular 8

I am trying to loop over an array of objects and display it in html using Angular 8.
My app.component.ts file is as shown below:
In the above code uptimeDataSet is the array of data that I need to loop over and display on another component.
My app.component.html file looks as shown below:
Finally, I am trying to loop over in my component to display the object in uptime-chart-component.html as below:
But I am not able to get the details in the UI. I am not able to find out where I am going wrong. Can someone please help me with this.
Update your uptime-chart.component.ts file-
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-uptime-chart',
templateUrl: './uptime-chart.component.html',
styleUrls: ['./uptime-chart.component.css']
})
export class UptimeChartComponent implements OnInit {
#Input() chartData: any[];
constructor() { }
ngOnInit(): void {
}
}
You need use #Input to capture the value in your child component. #Input decorator is used when you are passing data from parent to child. I suggest you take a look at this blog to understand- https://fireship.io/lessons/sharing-data-between-angular-components-four-methods/
And I believe, you can change your app.component.html without the *ngFor-
<app-user [chartData]="uptimeDataSet"></app-user>
if this is what you want your result to be

Angular 2. How dynamically created component could call by itself the destroy method?

I got stuck with the destroying of dynamic components. I would be really appreciate for some hints.
Here my root component, it adds perfectly on a page some components from a service:
/*Root*/
#Component({
selector: 'convertors',
template: "<div #target></div>"})
export class AppComponent {
#ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef;
private componentRef: ComponentRef<any>;
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef){}
addComponent(){
let someComponent = this.service.getService("someComponent");
const factory = this.componentFactoryResolver.resolveComponentFactory(someComponent);
this.componentRef = this.target.createComponent(factory);
}}
Here my child component which is added by root component. It has to be self destroyed:
#Component({
selector: 'convertors',
template: "<button (click)="deleteComponent()" >Delete</button>"})
export class someComponent{
deleteComponent(){
/*How could I implement this function?*/
}
}
How could I implement the method deleteComponent() ?
Thank you!
Ok, my solution uses message service. Look here if you don't know what it does. it's very simple.
Firstly, I assign unique id to any dynamic component. Next I keep reference on the component and his id in a map.
private componentRef: ComponentRef<Component>;
onComponent(event: string){
let component = "ref to any component"
const factory = this.componentFactoryResolver.resolveComponentFactory(component);
let componentRef = this.target.createComponent(factory);
let id = "id" + this.index;
(<any>componentRef.instance).componentId = id;
this.idMap.set(id, componentRef);
this.componentRef = componentRef;
this.index += 1;
}
Secondly, event click on child's view sends his id to parent through message service. Take in account that "componentId: string" declared in a child. But it is assigned in the parent!
<a (click)="deleteComponent()"></a>
private componentId: string;
deleteComponent() : void {
this.messageService.sendMessage(this.componentId);
}
Lastly, our parent receives from message service the id. Next, it looks for child's reference in the map. Finally, we call native method this.target.remove("child index in componentRef") to remove component. Take in account, that componentRef has his own indexes of his children.
this.subscription = this.messageService.getMessage()
.subscribe(message => this.removeChild(message))
removeChild(message){
let component = this.idMap.get(message.id);
let indice = this.target.indexOf(component);
this.target.remove(indice);
this.idMap.delete(message.id);
}
If know how improve this solution write in commments. I have the feeling that it could be better. Thanks.

Getting element height

I was curious if I can get element properties form component template.
So I have made simple div with class and I've made this class:
export class viewApp{
elementView: any;
viewHeight: number;
myDOM: Object;
constructor() {
this.myDOM = new BrowserDomAdapter();
}
clickMe(){
this.elementView = this.myDOM.query('div.view-main-screen');
this.viewHeight = this.myDOM.getStyle(this.elementView, 'height');
}
}
getStyle(), query() are from BrowserDomAdapter.
My problem is when I try to get height it is null, but when I set some height by setStyle() and then I get it by getStyle() it returns proper value.
After checking DOM and styles in browser I discovered that is because of two CSS elements. One is: .view-main-screen[_ngcontent-aer-1]{} and second one is element{}.
.view-main-screen has some stylings, but element is empty. When I add styles by setStyle() it appears in element{}. Why is that? How can I get element properties by using Angular2?
The correct way is to use #ViewChild() decorator:
https://angular.io/docs/ts/latest/api/core/index/ViewChild-decorator.html
Template:
<div class="view-main-screen" #mainScreen></div>
Component:
import { ElementRef, ViewChild } from '#angular/core';
export class viewApp{
#ViewChild('mainScreen') elementView: ElementRef;
viewHeight: number;
constructor() {
}
clickMe(){
this.viewHeight = this.elementView.nativeElement.offsetHeight;
}
}
That should do it but obviously you need to add your Component decorator.
Edit:
For Angular 8 or later you need to provide the 2nd parameter in ViewChild
#ViewChild('mainScreen', {read: ElementRef, static:false}) elementView: ElementRef;
update2
constructor(private elementRef:ElementRef) {}
someMethod() {
console.log(this.elementRef.nativeElement.offsetHeight);
}
Accessing nativeElement directly is discouraged but current Angular doesn't provide other ways as far as I know.
update
https://github.com/angular/angular/pull/8452#issuecomment-220460761
mhevery commented 12 days ago
We have decided to remove Ruler service, and so it is not part of the public API.
original
As far as I know the Ruler class should provide that functionality
https://github.com/angular/angular/blob/master/modules/angular2/src/platform/browser/ruler.ts if this isn't enought you probably need to access elementRef.nativeElement and use direct DOM access and functions provided by the elements.
new Ruler(DOM).measure(this.elRef).then((rect: any) => {
});
Rules service is safe in WebWorker.
See also the comments on https://github.com/angular/angular/issues/6515#issuecomment-173353649
<div #getElementHeight>
Height
</div>
Height of element is {{ getElementHeight.offsetHeight }}
<div *ngFor="let item of items" (click)="itemClick($event.currentTarget)"></div>
itemClick(dom){
var height=dom.clientHeight;
// ...
}