一位开发人员展示了他为Angular和Spring Boot编写的大量代码,他用它创建了一个完整的堆栈Web应用程序。
该MovieManager项目用于管理我的电影的收集和使用角度和Spring启动PostgreSQL的做到这一点。
该项目用于演示如何为电影标题构建自动完成搜索框。自动完成框在前端使用Bootstrap 4,在后端使用PostgreSQL的Spring Boot。重点在于如何将本地Angular / RxJS与Spring Boot以及关系数据库一起使用。
前端
前端使用Angular与RxJS和Bootstrap 4进行造型。Angular和RxJS具有创建输入并将其连接到后端REST服务的功能。
然后,在每次击键之后,服务的结果将在输入下方的div中显示和更新。Bootstrap提供CSS类来设置组件的样式。
前端有三部分:
带输入标签和输出div的div模板。
具有表单控件的组件类,用于将输入标记与服务方法相连接。
向服务器发出REST请求的服务类。
在搜索组件中,我使用了以下 模板:
...
Searchfor Movie Title
Loading
...
第1-2行创建一个可变宽度和一行的Bootstrap容器。
第4行为自动填充功能创建引导列。
第5-8行创建一个输入组,它用一个标签显示样式化输入,并将Angular form控件链接movieTitle 到输入。
如果在输入中输入了某些内容,第9行将创建结果div。
如果服务从服务器检索电影,则第10行显示文本“加载”。
第11行显示了从服务器加载的电影的路由器链接。这 routerLink 是电影组件与电影ID的路由。 *ngFor 对服务器发送的电影进行迭代,并且 async 意味着电影是在到达时显示其数据的观察者。链接的文本是电影标题。
在搜索组件中,我使用了以下类:
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
generes: Genere[];
movieTitle = new FormControl();
movies: Observable
; movieActor = new FormControl();
actors: Observable
; importMovies: Movie[] = [];
importMovieTitle = new FormControl();
actorsLoading = false;
moviesLoading = false;
importMoviesLoading = false;
showMenu = false;
moviesByGenere: Movie[] = [];
moviesByGenLoading = false;
constructor(private actorService: ActorsService, private movieService: MoviesService, private userService: UsersService) { }
ngOnInit() {
this.actors = this.movieActor.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.do(() => this.actorsLoading = true)
.switchMap(name => this.actorService.findActorByName(name))
.do(() => this.actorsLoading = false);
this.movies = this.movieTitle.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.do(() => this.moviesLoading = true)
.switchMap(title => this.movieService.findMovieByTitle(title))
.do(() => this.moviesLoading = false);
this.userService.allGeneres().subscribe(res => this.generes = res);
}
第1-6行使用注释和OnInit 界面创建搜索组件。
第9行为movieTitle 用户输入创建 表单控件。
第16行创建 moviesLoading 布尔值来显示加载文本。
第31行将movieTitle 表单控件与 valueChanges observable和Observable结果链接起来movies。
第32-33行在请求到后端之间添加了400 ms的超时时间,并且只在输入发生更改时才发送请求。
第34行设置显示模板中加载文本的布尔值。
第35行用于 switchMap 丢弃正在运行的请求并向服务发送新的请求。
第36行设置将加载文本设置为false的布尔值。
这是管理所有电影数据的电影服务:
@Injectable()
export class MoviesService {
private _reqOptionsArgs = { headers: new HttpHeaders().set( 'Content-Type', 'application/json' ) };
constructor(private http: HttpClient) { }
...
public findMovieByTitle(title: string) :Observable
{ if(!title) {
return Observable.of([]);
}
return this.http.get('/rest/movie/'+title, this._reqOptionsArgs).catch(error => {
console.error( JSON.stringify( error ) );
return Observable.throw( error );
});
}
...
}
第1-2行MoviesService 用注释创建。
第3行创建HTTP头。
第5行是获取注入的HttpClient的构造函数。
Observable 如果标题为空,则第10-12行将返回 空白。
第13-16行将HTTP请求发送到服务器,捕获服务器错误,记录它们并引发错误。
后端
后端在Spring Boot中使用JPA和PostgreSQL完成。它使用Spring Boot功能为REST界面和服务提供服务,以管理存储库并围绕服务调用创建事务。这些事务需要支持MovieManager中的电影功能的导入和删除。
存储库使用JPA创建一个查询,查找包含(区分大小写)用户输入的标题字符串的用户电影的电影标题。
REST服务是在这个类中创建的:
@RestController
@RequestMapping("rest/movie")
public class MovieController {
@Autowired
private MovieManagerService service;
...
@RequestMapping(value="/{title}", method=RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity
> getMovieSearch(@PathVariable("title") String titleStr) throws InterruptedException {
List
movies = this.service.findMovie(titleStr); return new ResponseEntity
>(movies, HttpStatus.OK);
}
...
}
第5-6行注入了MovieManagerService。它管理事务,并且是电影数据的公共服务。
第10-11行设置了RequestMapping并获得请求字符串的标题值。
第12行从这个标题字符串的MovieManagerService读取电影列表。
第13行将电影列表包装在ResponseEntity中并返回它。
主要的电影服务是在这个类中创建的:
@Transactional
@Service
public class MovieManagerService {
@Autowired
private CrudMovieRepository crudMovieRep;
@Autowired
private CrudCastRepository crudCastRep;
@Autowired
private CrudActorRepository crudActorRep;
@Autowired
private CrudGenereRepository crudGenereRep;
@Autowired
private CrudUserRepository crudUserRep;
@Autowired
private CustomRepository customRep;
@Autowired
private AppUserDetailsService auds;
...
public List
findMovie(String title) { List
result = this.customRep.findByTitle(title).stream() .map(m -> Converter.convert(m)).collect(Collectors.toList());
return result;
}
...
}
第14-15行获得 CustomRepository 注入。这是所有非CRUD DB请求的回购。
第23-24行将Movie Dtos中的Movie实体列表转换为可以由该序列化 RestController 并返回Dto列表。
自定义库在此创建类:
@Repository
public class CustomRepository {
@PersistenceContext
private EntityManager em;
@Autowired
private CrudUserRepository crudUserRep;
...
public List
findByTitle(String title) { User user = getCurrentUser();
List
results = em.createQuery("select e from Movie e join e.users u where e.title like :title and u.id = :userid", Movie.class).setParameter("title", "%"+title+"%").setParameter("userid", user.getId()).getResultList(); return results;
}
...
}
第3-4行获得注入Jpa的EntitiyManager。
第5-6行获取注入的CrudUserRepository以访问当前登录的用户。多用户支持需要当前用户。
第12-13行向EntityManager查询电影标题包含标题字符串和当前用户是电影所有者的电影。为此,电影和用户实体需要被连接,然后通过电影标题和userId进行过滤。然后返回结果。
概要
Angular和RxJ具有在几行代码中创建自动完成的功能。诸如击键之间的超时,没有双重请求以及丢弃陈旧结果之类的功能由本地功能支持。链接到valueChanges observable 的表单控件 将更新div中的结果。
DZone有一篇很好的文章,描述了Angular及其一些功能。Java开发人员熟悉依赖注入,静态类型和注释等许多功能,并使Angular易于学习。这使得Angular成为对前端开发感兴趣的Java开发人员的非常好的框架。
后端是带有服务和JPA存储库的小型REST控制器。Spring Boot使得用几行代码和几个注释创建后端成为可能。
閱讀更多 愛碼農 的文章