来自Subject / Services的角度4/5组件变量不绑定到HTML模板 [英] Angular 4/5 Component variable from Subject/Services not binding to HTML template
问题描述
我的app.component.ts文件中有一个变量,通过我的services.ts文件从控制器传入。
就我所见,通过Subject传递给Component的Object属性正在工作,因为我可以 console.log(this。 selectedContact.name)
,它将完全登录到控制台,这意味着它正确存储到变量 selectedContact:any;
中。但我似乎无法将其绑定到我的HTML,我很好奇我可能会丢失什么。
这是我试图绑定到的视图的app.component.ts文件:
从'@ angular / core'导入{Component,OnInit};
从'../contacts-list/contacts-list.component'中导入{ContactsListComponent};
从'../api.service'中导入{ApiService};
@Component({
selector:'app-contact-details',
templateUrl:'./contact-details.component.html',
styleUrls: ['./contact-details.component.scss']
})
导出类ContactDetailsComponent实现OnInit {
selectedContact:any;
构造函数(private _apiService:ApiService){}
$ b ngOnInit(){this.showContact()}
showContact(){
this._apiService.newContactSubject.subscribe(
contact => {
this.selectedContact = contact;
console.log(this.selectedContact.name);
});
$ b这是我的HTML文件(我正在尝试的视图绑定:
< div id ='contact-card'>
< header>
<按钮routerLink ='/ home'><通讯录< /按钮>
< / header>
<部门ID =卡片容器>
< img id =largeContactPhotoonerror =onerror = null; src ='.. / assets / User Large / User - Large.png';
src = {{selectedContact.largeImageURL}} />
< h1> {{selectedContact.name}}< / h1>
< h2> {{selectedContact.companyName}}< / h2>
< ul>
< ; hr>
< li class =contact-data>
< h3>电话:< / h3>
{{selectedContact.phone.home}}
< hr>
< / li>
< li class =contact-data>
< h3>电话:< / h3>
{ {selectedContact.phone.mobile}}
< hr>
< / li>
< li class =contact-data>
< h3>电话:< / h3>
{{selectedContact.phone.work}}
< hr>
< / li>
< li class =contact-data>
< h3>地址:< / h3>
{{selectedContact.address.street}}< br>
{{selectedContact.address.city}},{{selectedContact.address.state}} {{selectedContact.address.zipcode}}
< hr>
< / li>
< li class =contact-data>
< h3>出生日期:< / h3>
{{selectedContact.birthdate}}
< hr>
< / li>
< li class =contact-data>
< h3>电子邮件地址:< / h3>
{{selectedContact.email}}
< hr>
< / li>
< / ul>
< / section>
< / div>
app.service.ts(其中点击事件数据传递给app.component.ts:
从'@ angular / core'导入{Injectable};
从'@angular'导入{Http,Response} / http';
从'rxjs / Observable'导入{Observable};
从'rxjs / Subject'导入{Subject};
导入'rxjs / add / operator / map';
@Injectable()
export class ApiService {
// API URL
private url:string ='assets / api / contacts.json';
public newContactSubject = new Subject< any>();
//为请求
构造函数(private _http:Http){}
初始化HttpClient
//从REST API中提取JSON数据
getContacts():Observable< any> {
返回this._http.get(this.url)
.map((response:响应)=> response.json());
}
openSelectedContact(data){
this.newContactSubject.next(data);
}
}
同步组件这是点击事件发生的地方* :
从'@ angular / core'导入{Component,OnInit};
从'@ angular / router'导入{Router};
从'../api.service'中导入{ApiService};
@Component({
选择器:'app-contacts-list',
templateUrl:'./contacts-list.component.html',
styleUrls: ['./contacts-list.component.scss']
})
export class ContactsListComponent实现OnInit {
title:string ='Contacts';
sortedFavorites:any [] = [];
sortedContacts:any [] = [];
errorMessage:string;
构造函数(private _apiService:ApiService,private router:Router){}
$ b ngOnInit(){this.getContacts()}
getContacts ){
this._apiService.getContacts()
.subscribe(
(contacts)=> {
//按字母顺序排序JSON对象
contacts (a.name> b.name){return 1};
if(a.name< b.name){return - 1};
return 0;
})
//建立新的排序数组
contacts.forEach((item)=> {
if (item.isFavorite){
this.sortedFavorites.push(item);
} else {
this.sortedContacts.push(item);
}
}) ;
});
}
openFavorite($ event,i){< - 从HTML模板进来的点击事件
let selectedFavorite = this.sortedFavorites [i];
this._apiService.openSelectedContact(selectedFavorite); < - 将此数据路由到services.ts文件
this.router.navigate(['/ details']); <导航到详细视图以查看选定的联系人。
};
这个绑定可以和任何其他的测试变量一起使用,到我的app.component.ts文件中,并且它们被绑定到页面。我觉得这是正确的语法,它是正确绑定object.properties,但它似乎并没有工作。
我可能会缺少什么这个?编辑之一:我觉得我还应该补充说,这个完全相同的代码 是在我有过之前工作的时候 我的组件路由到这个组件。只有当我将文件移动到一个新组件并创建了一个不同的新组件时,才发生这种情况。也许它没有正确导入某处?
编辑二 :我重构了原始代码发布并更正了我的TypeScript声明,并从我的html模板中删除了 this
。我在 showContact()
方法中放置了 console.log(this.selectedContact.name)
函数,记录正确的名字,一旦联系人列表中的名称被点击并伴随着这个错误: ERROR TypeError:无法读取undefined
属性'largeImageURL'。所以它正确地记录 this.selectedContact.name
,并且该变量没有在我的应用程序的其他地方定义。我已经添加了我的app.service.ts文件以及我的兄弟组件.ts文件,以便将其放入发生点击事件的上下文中。我希望点击事件数据从Sibling组件 - > Api Service.ts文件 - >单击事件中的兄弟组件。这非常令人困惑,为什么这种情况没有发生。
编辑三
其他我注意到的问题是,每当我尝试使用安全导航操作符来解决此问题时,我的突出显示会在我的html模板中发生变化,使我认为它正在破坏它。
与此相比:
注意< / h1
>突出显示?这使得每个 开启和关闭标记都显示为灰色,而不是现在的红色。我从来没有试图使用安全的导航操作符,所以我不确定这是否正常。我已经在和{code>}}
周围使用了不同的空格,并且仍然没有运气。
解决方案您是否期待数组或对象?您应该输入您从服务中获得的内容。
在这里你提到一个数组:
selectedContact:any [] = [];
在这里您提到一个对象。
< H1> {{this.selectedContact.name}}< / h1>
在模板中使用 json
管道。
{{selectedContact | json}}
这将帮助您调试。
此外,因为您的数据在您的服务收到之前会在浏览器中显示,您将收到未定义的变量。可以使用?
或 * ngIf
。 (使用* ngIf,不要将您的selectedContact定义为一个数组)开始。
< div id ='contact -card'* ngIf =selectedContact>
或
< H1> {{this.selectedContact?.name}}< / h1>
I have a variable in my app.component.ts file that is passed in from a controller via my services.ts file.
As far as I can see, the Object property passed to the Component via the Subject is working, because I can console.log(this.selectedContact.name)
and It will log to the console perfectly, which would mean that it is correctly being stored into the variable selectedContact: any;
. But I cannot seem to bind this to my HTML and I am curious as to what I may be missing.
Here is my app.component.ts file for the view I'm attempting to bind to:
import { Component, OnInit } from '@angular/core';
import { ContactsListComponent } from '../contacts-list/contacts-list.component';
import { ApiService } from '../api.service';
@Component({
selector: 'app-contact-details',
templateUrl: './contact-details.component.html',
styleUrls: ['./contact-details.component.scss']
})
export class ContactDetailsComponent implements OnInit {
selectedContact: any;
constructor(private _apiService: ApiService) { }
ngOnInit() { this.showContact()}
showContact() {
this._apiService.newContactSubject.subscribe(
contact => {
this.selectedContact = contact;
console.log(this.selectedContact.name);
});
}
}
Here is my HTML file (The view I am attempting to bind to.:
<div id= 'contact-card' >
<header>
<button routerLink='/home'>< Contacts</button>
</header>
<section id= card-container>
<img id ="largeContactPhoto" onerror="onerror=null;src='../assets/User Large/User — Large.png';"
src={{selectedContact.largeImageURL}} />
<h1> {{selectedContact.name}} </h1>
<h2> {{selectedContact.companyName}} </h2>
<ul>
<hr>
<li class="contact-data">
<h3> Phone: </h3>
{{selectedContact.phone.home}}
<hr>
</li>
<li class="contact-data">
<h3> Phone: </h3>
{{selectedContact.phone.mobile}}
<hr>
</li>
<li class="contact-data">
<h3> Phone: </h3>
{{selectedContact.phone.work}}
<hr>
</li>
<li class="contact-data">
<h3> Address: </h3>
{{selectedContact.address.street}}<br>
{{selectedContact.address.city}},{{selectedContact.address.state}} {{selectedContact.address.zipcode}}
<hr>
</li>
<li class="contact-data">
<h3> Birthdate: </h3>
{{selectedContact.birthdate}}
<hr>
</li>
<li class="contact-data">
<h3> Email: </h3>
{{selectedContact.email}}
<hr>
</li>
</ul>
</section>
</div>
app.service.ts (where the click event data is being passed to app.component.ts:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map';
@Injectable()
export class ApiService {
//API URL
private url: string = 'assets/api/contacts.json';
public newContactSubject = new Subject<any>();
//Initialize HttpClient for request
constructor(private _http: Http) { }
//Pull JSON data from REST API
getContacts(): Observable<any> {
return this._http.get(this.url)
.map((response: Response) => response.json());
}
openSelectedContact(data) {
this.newContactSubject.next(data);
}
}
Sibling Component THIS IS WHERE THE CLICK EVENT HAPPENS* :
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ApiService } from '../api.service';
@Component({
selector: 'app-contacts-list',
templateUrl: './contacts-list.component.html',
styleUrls: ['./contacts-list.component.scss']
})
export class ContactsListComponent implements OnInit {
title : string = 'Contacts';
sortedFavorites: any[] = [];
sortedContacts: any[] = [];
errorMessage: string;
constructor (private _apiService: ApiService, private router: Router) {}
ngOnInit(){ this.getContacts()}
getContacts() {
this._apiService.getContacts()
.subscribe(
(contacts) => {
//Sort JSON Object Alphabetically
contacts.sort((a , b) => {
if (a.name > b.name) {return 1};
if (a.name < b.name) {return -1};
return 0;
})
//Build new Sorted Arrays
contacts.forEach( (item) => {
if (item.isFavorite) {
this.sortedFavorites.push(item);
} else {
this.sortedContacts.push(item);
}
});
});
}
openFavorite($event, i) { <--Click event coming in from HTML template
let selectedFavorite = this.sortedFavorites[i];
this._apiService.openSelectedContact(selectedFavorite); <--Routing this data to services.ts file
this.router.navigate(['/details']); <--Navigate to detailed view to see selected contact.
};
}
This binding will work with any other test variables that i put into my app.component.ts file and they get bounded to the page. I feel like this is the right syntax for it to be properly binding the object.properties, but it doesn't seem to be working.
What might I be missing on this? Thanks a lot in advance!!
EDIT One: I feel like I should also add that this exact same code was working before when I had my component routed to this component. It was only when I moved my files to a new component and created a different new that this started happening. Maybe it is isn’t imported correctly somewhere?
EDIT TWO: I have refactored the code in the original post and corrected my TypeScript declarations and removed this
from my html template. I have placed my console.log(this.selectedContact.name)
function inside my showContact()
method and it does log the correct name once the name in the contacts list is clicked along side this error: ERROR TypeError: Cannot read property 'largeImageURL' of undefined
. So it is logging this.selectedContact.name
correctly and that variable isn't defined anywhere else in my application. I have added my app.service.ts file as well as my sibling component.ts file just to put this in context where the click event happens. I want the click event data to go from Sibling component --> Api Service.ts file --> Sibling component on a click event. This is very confusing to me why this isn't happening.
EDIT THREE
One other issue that I have noticed is that whenever I attempt to use the safe navigation operator to resolve this, my highlighting changes in my html template, making me think that it is breaking it.
versus this:
Notice the change in the </h1
> highlighting? This has subsiquently made every open and closing tag show up in grey instead of the red now. I have never attempted to use the safe navigation operator, so I am unsure as to whether this is normal. I have tried it with different spacing around the {{
and }}
and still no luck.
解决方案 Are you expecting an array or an object? You should type exactly what you are getting from your service.
Here you mention an array:
selectedContact: any[] = [];
Here you mention an object.
<h1> {{this.selectedContact.name}} </h1>
Give a shot in your template with the json
pipe.
{{ selectedContact | json }}
This will help you debug.
Also, because your data is displayed on the browser before it is received by your service, you will get an undefined variable. Either use ?
or an *ngIf
. (With the *ngIf, don't define your selectedContact as an array to start with)
<div id= 'contact-card' *ngIf="selectedContact">
or
<h1> {{this.selectedContact?.name}} </h1>
这篇关于来自Subject / Services的角度4/5组件变量不绑定到HTML模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!