Angular报错TypeError: Cannot read 'email' of undefined
问题根因
控制台报错TypeError: Cannot set properties of undefined (setting 'email')翻译:类型错误:无法为undefined对象设置属性(正在赋值'email'字段),触发原因以及后端接参为null的核心问题有3个:
- 模板中双向绑定
[(ngModel)]使用了仅支持读操作的安全导航符?,且实例化CreateUser时没有初始化嵌套的loginDO属性,loginDO初始为undefined,表单输入时Angular尝试给不存在的对象赋值直接抛错,输入的邮箱、密码根本没有绑定到userDO实例上,最终发请求时loginDO字段为空,后端自然接不到有效值。 LoginService中的HTTP POST是异步请求,原代码在接口还没返回响应时就同步return了初始空对象,组件层永远拿不到后端真实返回的用户数据。- 若后端Spring Boot接口的参数未加
@RequestBody注解,即使前端传了正确JSON,Spring也无法将请求体解析为Java对象,同样会出现参数全为null的情况。
修复步骤
1. 修复数据模型初始化,移除ngModel中的安全导航符
首先修改CreateUser类,实例化时直接初始化嵌套的loginDO属性,避免出现undefined:
// CreateUser.ts import { LoginUser } from "./LoginUser"; export class CreateUser { firstName: string = ''; lastName: string = ''; birthdayDate: string = ''; gender: string = ''; country: string = ''; phoneNumber: string = ''; loginDO: LoginUser = new LoginUser(); }
再修改LoginUser类,给字段设置初始空值:
// LoginUser.ts export class LoginUser { email: string = ''; password: string = ''; }
最后修改模板中两个输入框的ngModel绑定,去掉?安全导航符(此时loginDO已经确定初始化,不存在undefined问题):
<!-- 邮箱输入框 --> <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" name="email" [(ngModel)]="userDO.loginDO.email" /> <!-- 密码输入框 --> <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" name="password" [(ngModel)]="userDO.loginDO.password" />
2. 修复异步请求逻辑,禁止同步返回异步结果
将LoginService中的方法改为直接返回HTTP请求的Observable对象,不要在Service中提前订阅、同步return空值:
// login.service.ts import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { CreateUser } from '../CreateUser'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LoginService { private baseUrl:string = "http://localhost:8080"; constructor(private http:HttpClient) {} fetchUser(userData: CreateUser): Observable<CreateUser> { return this.http.post<CreateUser>(`${this.baseUrl}/login`, userData); } }
对应修改组件中的调用逻辑,订阅Observable获取后端返回值:
// login.component.ts import { Component, OnInit } from '@angular/core'; import { CreateUser } from 'src/app/CreateUser'; import { LoginService } from 'src/app/service/login.service'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { userDO: CreateUser = new CreateUser(); constructor(private postRequest : LoginService) {} loginUser() { console.log("提交的表单数据:", this.userDO); this.postRequest.fetchUser(this.userDO).subscribe({ next: (res) => { console.log("后端返回用户数据:", res); this.userDO = res; // 此处写登录成功后的路由跳转、本地存储等逻辑 }, error: (err) => { console.error("登录请求失败:", err); } }) } ngOnInit(): void {} }
3. 校验后端接口配置
确认Spring Boot的登录接口方法给入参加了@RequestBody注解,参考示例:
@PostMapping("/login") // 必须加@RequestBody才能解析前端传的JSON请求体 public CreateUser login(@RequestBody CreateUser userDO) { // 此处编写账号密码校验逻辑 // 此时userDO里的loginDO.email、loginDO.password可以正常获取到前端传值 }
如果Angular(默认4200端口)和Spring Boot(默认8080端口)端口不一致出现跨域报错,在Controller类上添加@CrossOrigin(origins = "http://localhost:4200")注解,或配置全局跨域规则即可。
内容的提问来源于stack exchange,提问作者Jay Mistry
相关产品推荐
相关产品推荐

