constpath=require('path');constwebpack=require('webpack');module.exports={entry:{polyfills:'./src/polyfills.ts',app:'./src/main.ts',},output:{path:path.resolve(__dirname,'./public'),// путь к каталогу выходных файлов — папка publicpublicPath:'/public/',filename:'[name].js',// название создаваемого файла},devServer:{historyApiFallback:true,},resolve:{extensions:['.ts','.js'],},module:{rules:[//загрузчик для ts{test:/\.ts$/,// определяем тип файловuse:[{loader:'awesome-typescript-loader',options:{configFileName:path.resolve(__dirname,'tsconfig.json'),},},'angular2-template-loader',],},{test:/\.html$/,loader:'html-loader',},],},plugins:[newwebpack.ContextReplacementPlugin(/angular(\\|\/)core/,path.resolve(__dirname,'src'),// каталог с исходными файлами{}// карта маршрутов),],};
Затем в проекте создадим папку src. А в этой папке создадим каталог app и в начале определим в нем файл user.ts, который будет описывать используемые данные:
Класс User представляет пользователя и содержит три общедоступных поля id (уникальный идентификатор), name (имя) и age (возраст).
Все данные, описываемые классом User, будут храниться на сервере в базе данных. Поэтому нам необходим сервис для взаимодействия с сервером. И для этой цели в папке src/app создадим новый файл user.service.ts, в котором определим класс UserService:
Для сервиса определен url для всех запросов. По этому url будет запущено приложение сервера. Оно может представлять любую серверную технологию: PHP, Node.js, ASP.NET. Для отправки запросов GET/POST/PUT/DELETE сервис использует соответствующие методы get()/post()/put()/delete() из объета http.
Далее добавим в папку src/app файл компонента app.component.ts:
import{TemplateRef,ViewChild}from'@angular/core';import{Component,OnInit}from'@angular/core';import{User}from'./user';import{UserService}from'./user.service';import{Observable}from'rxjs';@Component({selector:'my-app',templateUrl:'./app.component.html',providers:[UserService],})exportclassAppComponentimplementsOnInit{//типы шаблонов@ViewChild('readOnlyTemplate',{static:false})readOnlyTemplate:TemplateRef<any>;@ViewChild('editTemplate',{static:false})editTemplate:TemplateRef<any>;editedUser:User;users:Array<User>;isNewRecord:boolean;statusMessage:string;constructor(privateserv:UserService){this.users=newArray<User>();}ngOnInit(){this.loadUsers();}//загрузка пользователейprivateloadUsers(){this.serv.getUsers().subscribe((data:User[])=>{this.users=data;});}// добавление пользователяaddUser(){this.editedUser=newUser(0,'',0);this.users.push(this.editedUser);this.isNewRecord=true;}// редактирование пользователяeditUser(user:User){this.editedUser=newUser(user.id,user.name,user.age);}// загружаем один из двух шаблоновloadTemplate(user:User){if(this.editedUser&&this.editedUser.id==user.id){returnthis.editTemplate;}else{returnthis.readOnlyTemplate;}}// сохраняем пользователяsaveUser(){if(this.isNewRecord){// добавляем пользователяthis.serv.createUser(this.editedUser).subscribe((data)=>{(this.statusMessage='Данные успешно добавлены'),this.loadUsers();});this.isNewRecord=false;this.editedUser=null;}else{// изменяем пользователяthis.serv.updateUser(this.editedUser.id,this.editedUser).subscribe((data)=>{(this.statusMessage='Данные успешно обновлены'),this.loadUsers();});this.editedUser=null;}}// отмена редактированияcancel(){// если отмена при добавлении, удаляем последнюю записьif(this.isNewRecord){this.users.pop();this.isNewRecord=false;}this.editedUser=null;}// удаление пользователяdeleteUser(user:User){this.serv.deleteUser(user.id).subscribe((data)=>{(this.statusMessage='Данные успешно удалены'),this.loadUsers();});}}
Так как каждая строка грида может быть в двух состояниях — в режиме редактирования и в режиме просмотра, то соответственно определяем с помощью декоратора ViewChild две переменных readOnlyTemplate и editTemplate, через которые мы будем ссылаться на используемые для строк шаблоны. Каждая переменная представляет тип TemplateRef. TemplateRef используется для создания вложенных представлений.
Для хранения редактируемого пользователя определена переменная editedUser, а для хранения списка пользователей — переменная users.
В методе ngOnInit вызывается метод loadUsers, в котором происходит загрузка данных с помощью сервиса UserService в список users.
В методе addUser() добавляется новый объект User. При этом добавляемый объект помещается в переменную editedUser и затем добавляется в массив users. И кроме того, для переменной isNewRecord устанавливается значение true. Это позволит идентифицировать в дальнейшем объект как именно как объект для добавления.
Метод editUser() получает объект User, который надо отредактировать, и передает его переменной editedUser.
Метод loadTemplate() позволяет загрузить для определенного объекта User нужный шаблон. То есть, как было сказано выше, строка грида может находиться в двух состояниях, и соответственно у нас будет два шаблона: для просмотра и для редактирования. Объект, для которого надо загрузить шаблон, передается в качестве параметра. И если определена переменная editedUser и ее свойство Id совпадает со значением свойства Id у того объекта, для которого надо загрузить шаблон, то выбирается шаблон для редактирования. Иначе же загружается шаблон для просмотра.
В методе saveUser() в зависимости от значения переменной isNewRecord данные отправляются на сервер либо через запрос типа POST (добавление нового объекта), либо через запрос типа PUT (редактирование объекта).
Метод cancel() сбрасывает редактирование.
И метод deleteUser() удаляет объект, отправляя через сервис UserService запрос к серверу.
И также добавим в проект в папку src/app новый файл app.component.html, который будет представлять шаблон для компонента AppComponent и который будет содержать следующий код:
<h1>Список пользователей</h1><inputtype="button"value="Добавить"class="btn btn-default"(click)="addUser()"/><tableclass="table table-striped"><thead><tr><td>Id</td><td>Имя</td><td>Возраст</td><td></td><td></td></tr></thead><tbody><tr*ngFor="let user of users"><ng-template[ngTemplateOutlet]="loadTemplate(user)"[ngTemplateOutletContext]="{$implicit:user}"></ng-template></tr></tbody></table><div>{{statusMessage}}</div><!--шаблон для чтения--><ng-template#readOnlyTemplatelet-user><td>{{user.id}}</td><td>{{user.name}}</td><td>{{user.age}}</td><td><inputtype="button"value="Изменить"class="btn btn-default"(click)="editUser(user)"/></td><td><inputtype="button"value="Удалить"(click)="deleteUser(user)"class="btn btn-danger"/></td></ng-template><!--шаблон для редактирования--><ng-template#editTemplate><td><inputtype="text"[(ngModel)]="editedUser.id"readonlydisabledclass="form-control"/></td><td><inputtype="text"[(ngModel)]="editedUser.name"class="form-control"/></td><td><inputtype="text"[(ngModel)]="editedUser.age"class="form-control"/></td><td><inputtype="button"value="Сохранить"(click)="saveUser()"class="btn btn-success"/></td><td><inputtype="button"value="Отмена"(click)="cancel()"class="btn btn-warning"/></td></ng-template>
С помощью директивы ngFor для каждого объекта из массива users создается строку с нужным шаблоном. Для встраивания шаблона в строку применяется элемент ng-template.
1234567
<tr*ngFor="let user of users"><ng-template[ngTemplateOutlet]="loadTemplate(user)"[ngTemplateOutletContext]="{$implicit:user}"></ng-template></tr>
С помощью директивы ngTemplateOutlet встраивается шаблон, который представляет объект TemplateRef. Эта директива привязана к методу loadTemplate(), который определен в классе AppComponent и который возвращает определенный шаблон.
А свойство ngTemplateOutletContext для передачи контекста в шаблон. С помощью параметра $implicit задается передаваемый объект. В данном случае это объект user.
В конце файла определены два шаблона для строк грида: readOnlyTemplate и editTemplate. Для определения шаблонов Angular использует элемент ng-template.
Шаблон readOnlyTemplate отображает объект User в режиме для чтения. Он содержит кнопки для редактирования и удаления объекта. Шаблон editTemplate определяет текстовые поля, которые привязаны к свойствам переменной editedUser из класса AppComponent. И также шаблон содержит кнопки для сохранения и отмены операции.
И также определим в папке src/app файл модуля приложения app.module.ts:
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Threading.Tasks;usingAngularMvcService.Models;usingMicrosoft.AspNetCore.Cors;usingMicrosoft.AspNetCore.Mvc;namespaceAngularMvcService.Controllers{[Route("api/[controller]")][enablecors("allowallorigin")]publicclassUsersController:Controller{ApplicationContextdb;publicUsersController(ApplicationContextcontext){db=context;}[HttpGet]publicIEnumerable<User>Get(){returndb.Users.ToList();}// GET api/users/5[HttpGet("{id}")]publicIActionResultGet(intid){Useruser=db.Users.FirstOrDefault(x=>x.Id==id);if(user==null)returnNotFound();returnnewObjectResult(user);}// POST api/users[HttpPost]publicIActionResultPost([FromBody]Useruser){if(user==null){returnBadRequest();}db.Users.Add(user);db.SaveChanges();returnOk(user);}// PUT api/users/[HttpPut]publicIActionResultPut([FromBody]Useruser){if(user==null){returnBadRequest();}if(!db.Users.Any(x=>x.Id==user.Id)){returnNotFound();}db.Update(user);db.SaveChanges();returnOk(user);}// DELETE api/users/5[HttpDelete("{id}")]publicIActionResultDelete(intid){Useruser=db.Users.FirstOrDefault(x=>x.Id==id);if(user==null){returnNotFound();}db.Users.Remove(user);db.SaveChanges();returnOk(user);}}/* используемая модель данныхpublic class User{ public int Id { get; set; } public string Name { get; set; } public int Age { get; set; }}*/}
Но естественно для приложения уровня сервера можно использовать любую другую технологию бекэнда: PHP, Node.js, Java и т.д.
Поле запуска приложения на сервере запустим приложение Angular. Если сервер возвратит какие-либо данные, то будут отображены в таблице с помощью шаблона readOnlyTemplate:
При нажатии на кнопку "Изменить" для редеринга строка используется шаблон editTemplate, и объект становится доступен для редактирования: