{"id":301495,"date":"2020-04-08T09:00:51","date_gmt":"2020-04-08T09:00:51","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=301495"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=301495","title":{"rendered":"Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 6 \u2014 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u043a\u0430\u0437\u0430 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044e\u0449\u0438\u043c\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u043c\u0438"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/496084\/\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/j4\/ty\/bj\/j4tybj7jllyqamrgzb-a2wlj5i4.png\"><\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e \u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d \u0438 \u0438\u0437\u0443\u0447\u0430\u0442\u044c Blazor. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 \u043d\u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0437\u0430\u043a\u0430\u0437\u043e\u0432 \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043e\u0434\u043d\u043e \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u043e\u0439. \u0417\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u043a\u0430\u0442. <br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/463197\/\">Blazor + MVVM = Silverlight \u043d\u0430\u043d\u043e\u0441\u0438\u0442 \u043e\u0442\u0432\u0435\u0442\u043d\u044b\u0439 \u0443\u0434\u0430\u0440, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0434\u0440\u0435\u0432\u043d\u0435\u0435 \u0437\u043b\u043e \u043d\u0435\u043f\u043e\u0431\u0435\u0434\u0438\u043c\u043e<\/a>  <\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/484596\/\">Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 1 \u2014 \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f oidc (oauth2) + Identity Server4<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/484782\/\">Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 2 \u2014 CI\/CD<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/494612\/\">Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 3 \u2014 \u0412\u0438\u0442\u0440\u0438\u043d\u0430 \u0442\u043e\u0432\u0430\u0440\u043e\u0432<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/495418\/\">Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 4 \u2014 \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/495812\/\">Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 5 \u2014 \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 Stateful<\/a><\/li>\n<li>Blazor Client Side \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u041c\u0430\u0433\u0430\u0437\u0438\u043d: \u0427\u0430\u0441\u0442\u044c 6 \u2014 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u043a\u0430\u0437\u0430 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044e\u0449\u0438\u043c\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u043c\u0438<\/li>\n<\/ul>\n<p>  <\/p>\n<h2>\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<p>  \u2192\u00a0<a href=\"https:\/\/gitlab.com\/VictorWinbringer\/blazoreshop\" rel=\"nofollow\">\u0418\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438<\/a><br \/>  \u2192 <a href=\"https:\/\/hub.docker.com\/u\/victorcallidus\" rel=\"nofollow\">\u041e\u0431\u0440\u0430\u0437\u044b \u043d\u0430 Docker Registry<\/a><\/p>\n<h2>Saga<\/h2>\n<p>  \u041c\u043d\u0435 \u0441\u0442\u0430\u043b\u043e \u0441\u043a\u0443\u0447\u043d\u043e \u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u2014 \u0434\u043b\u044f \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u043e\u0432. \u041e\u0431\u044b\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441 \u041e\u0440\u043a\u0435\u0441\u0442\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u043a\u043e\u0435 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0432 MassTransit, NServiceBus, <a href=\"https:\/\/github.com\/dotnet\/orleans\" rel=\"nofollow\">MS Orleans<\/a> (\u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438). \u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u0443\u0447\u0430\u0442\u044c\u0441\u044f \u0432 \u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c. Google \u0438 FaceBook \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440. \u0425\u043e\u0442\u044f \u0442\u0443\u0442 \u0442\u043e\u0436\u0435 \u043b\u0443\u0447\u0448\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0440\u0432\u0435\u0440, \u0430 \u043d\u0430 \u0431\u0435\u043a\u0435\u043d\u0434 \u043f\u043e\u0441\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441. \u0422\u0443\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440. \u0427\u0443\u0442\u044c \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0432 <a href=\"https:\/\/github.com\/Blazored\/LocalStorage\" rel=\"nofollow\">LocalStorage<\/a> \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438 \u043f\u043e \u0442\u0430\u0439\u043c\u0435\u0440\u0443 \u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u043e\u0442\u043a\u0430\u0442\u0438\u0442\u044c, \u0430 \u043d\u0435 \u0442\u0430\u043a \u0442\u043e\u043f\u043e\u0440\u043d\u043e \u043a\u0430\u043a \u044f \u0442\u0443\u0442 \u0441\u0434\u0435\u043b\u0430\u043b.<\/p>\n<p>  \u041a\u043e\u0434 OrdersViewModel:<\/p>\n<pre><code class=\"cs\">        public async Task Create()         {           \/\/\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.             await _basket.Load();             if (!string.IsNullOrWhiteSpace(_basket.Error))                 return;             \/\/\u0413\u043b\u0443\u0431\u043e\u043a\u043e\u0435 \u043a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043b\u0443\u0447\u0448\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u043f\u043f\u0435\u0440\u043e\u043c.             \/\/ \u0414\u043b\u044f \u0443\u0447\u0435\u0431\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0442\u0430\u043a \u0441\u043e\u0439\u0434\u0435\u0442.            \/\/\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043f\u0440\u0435\u0436\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u043b\u0443\u0447\u0448\u0435 \u0432 \u043b\u043e\u043a\u0430\u043b\u0441\u0442\u043e\u0440\u0430\u0436\u0435.             var lines = _basket.Model.Select(l =&gt; new LineModel()             {                 Product = new ProductModel()                 {                     Id = l.Product.Id,                     Price = l.Product.Price,                     Title = l.Product.Title,                     Version = l.Product.Version                 },                 Quantity = l.Quantity             }).ToList();           \/\/\u041f\u043e\u0441\u044b\u043b\u0430\u0435\u043c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 - \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043a\u043e\u0440\u0437\u0438\u043d\u0443.             await _basket.Clear();             if (!string.IsNullOrWhiteSpace(_basket.Error))                 return;            \/\/\u041f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0437\u0430\u043a\u0430\u0437 \u0438 \u0435\u0441\u043b\u0438 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0442\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u0435\u0436\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b.             try             {                 await _order.Create(lines, Address);             }             catch             {                 \/\/\u041a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0447\u0442\u043e\u0431\u044b \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u0435\u0436\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.                  await Restore(lines);                 throw;             }             if (!string.IsNullOrWhiteSpace(_order.Error))             {                 await Restore(lines);             }             State = OrderVmState.List;         }          private async Task Restore(IEnumerable&lt;LineModel&gt; lines)         {             \/\/\u0422\u0443\u0442 \u043d\u0430\u0434\u043e \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0437\u0443 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u044b             \/\/\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u043e \u043c\u043d\u0435 \u0434\u043b\u044f \u0443\u0447\u0435\u0431\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043b\u0435\u043d\u044c.            \/\/ \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0446\u0438\u043a\u043b\u0435 \u043f\u043e\u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u044b \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443.             foreach (var line in lines)             {                 for (int i = 0; i &lt; line.Quantity; i++)                 {                     await _basket.Add(line.Product);                 }             }         } <\/code><\/pre>\n<p>  <\/p>\n<h2>\u041a\u043e\u0434<\/h2>\n<p>  <\/p>\n<h3>1)Model<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">    public class LineModel     {         public uint Quantity { get; set; }         public ProductModel Product { get; set; }     }      public enum OrderStatus     {         Created = 10,         Delivered,     }      public class OrderModel     {         public Guid Id { get; set; }         public string Buyer { get; set; }         public OrderStatus Status { get; set; }         public List&lt;LineModel&gt; Lines { get; set; } = new List&lt;LineModel&gt;();         public string Address { get; set; }     } <\/code><\/pre>\n<p>  <\/p>\n<h3>2)Service<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">    public sealed class OrderService : IOrderService     {         private readonly IApiRepository _api;         private List&lt;OrderModel&gt; _orders;          public OrderService(IApiRepository api)         {             _api = api;             _orders = new List&lt;OrderModel&gt;();         }          public string Error { get; private set; }         public IReadOnlyList&lt;OrderModel&gt; Orders =&gt; _orders?.AsReadOnly();          public async Task Create(IEnumerable&lt;LineModel&gt; lines, string address)         {             var (_, e) = await _api.CreateOrder(lines, address);             Error = e;             if (!string.IsNullOrWhiteSpace(e))                 return;             await Load();         }          public async Task Load()         {             var (r, e) = await _api.GetOrders();             _orders = r;             Error = e;         }     } <\/code><\/pre>\n<p>  <\/p>\n<h3>3)ViewModel<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">    public class OrdersViewModel     {         private readonly IOrderService _order;         private readonly IBasketService _basket;          public OrdersViewModel(IOrderService order, IBasketService basket)         {             _order = order;             _basket = basket;             OrderFormContext = new EditContext(this);         }          public bool CanCreateOrder =&gt; _basket.ItemsCount &gt; 0;         public string Error =&gt; _order.Error + _basket.Error;         public IReadOnlyList&lt;OrderModel&gt; Model =&gt; _order.Orders;         public decimal Sum =&gt; _basket.Model.Sum(m =&gt; m.Quantity * m.Product.Price);         public EditContext OrderFormContext { get; }         public OrderVmState State { get; set; }         [Required]         [StringLength(255, MinimumLength = 3)]         public string Address { get; set; }          public void ChangeState(string value)         {             State = OrderVmState.List;             if (string.IsNullOrWhiteSpace(value))                 return;             if (Enum.TryParse(value, true, out OrderVmState state))                 State = state;             if (_basket.ItemsCount == 0 &amp;&amp; State == OrderVmState.Create)                 State = OrderVmState.List;         }          public async Task OnInitializedAsync()         {             await _order.Load();             await _basket.Load();         }          public async Task Create()         {             if (!OrderFormContext.Validate())                 return;             await _basket.Load();             if (!string.IsNullOrWhiteSpace(_basket.Error))                 return;             var lines = _basket.Model.Select(l =&gt; new LineModel()             {                 Product = new ProductModel()                 {                     Id = l.Product.Id,                     Price = l.Product.Price,                     Title = l.Product.Title,                     Version = l.Product.Version                 },                 Quantity = l.Quantity             }).ToList();             await _basket.Clear();             if (!string.IsNullOrWhiteSpace(_basket.Error))                 return;             try             {                 await _order.Create(lines, Address);             }             catch             {                 await Restore(lines);                 throw;             }             if (!string.IsNullOrWhiteSpace(_order.Error))             {                 await Restore(lines);             }             State = OrderVmState.List;         }          private async Task Restore(IEnumerable&lt;LineModel&gt; lines)         {             foreach (var line in lines)             {                 for (int i = 0; i &lt; line.Quantity; i++)                 {                     await _basket.Add(line.Product);                 }             }         }     } <\/code><\/pre>\n<p>  <\/p>\n<h3>4)View<\/h3>\n<p>  <\/p>\n<pre><code class=\"cs\">@page &quot;\/orders&quot; @page &quot;\/orders\/{operation}&quot;  @attribute [Authorize]  @inject OrdersViewModel ViewModel   &lt;h3&gt;Orders&lt;\/h3&gt; &lt;div&gt;     &lt;Error Model=&quot;@ViewModel.Error&quot; \/&gt; &lt;\/div&gt; @if (ViewModel.State == OrderVmState.Create) {     &lt;EditForm EditContext=&quot;@ViewModel.OrderFormContext&quot; OnValidSubmit=&quot;@ViewModel.Create&quot;&gt;         &lt;DataAnnotationsValidator \/&gt;         &lt;div class=&quot;form-group&quot;&gt;&lt;label class=&quot;form-label&quot;&gt; Sum: @ViewModel.Sum&lt;\/label&gt;&lt;\/div&gt;         &lt;div class=&quot;form-group&quot;&gt;             &lt;label class=&quot;form-label&quot; for=&quot;address&quot;&gt;Address&lt;\/label&gt;             &lt;InputTextArea id=&quot;address&quot; name=&quot;address&quot; class=&quot;form-control&quot; @bind-Value=&quot;@ViewModel.Address&quot; \/&gt;             &lt;ValidationMessage For=&quot;@(() =&gt; ViewModel.Address)&quot; \/&gt;         &lt;\/div&gt;         &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot; disabled=&quot;@(!context.Validate())&quot;&gt;Save&lt;\/button&gt;         &lt;button class=&quot;btn btn-default&quot; @onclick=&quot;@(x =&gt; ViewModel.State = OrderVmState.List)&quot;&gt;Cancel&lt;\/button&gt;     &lt;\/EditForm&gt; } else {     @if (ViewModel.CanCreateOrder)     {         &lt;input type=&quot;button&quot; class=&quot;btn btn-primary&quot; value=&quot;Create Order&quot; @onclick=&quot;@(x=&gt;ViewModel.State = OrderVmState.Create)&quot; \/&gt;     }     &lt;div class=&quot;table-responsive&quot;&gt;         &lt;table class=&quot;table&quot;&gt;             &lt;thead&gt;                 &lt;tr&gt;                     &lt;AuthorizeView Roles=&quot;admin&quot;&gt;                         &lt;Authorized&gt;                             &lt;th&gt;Id&lt;\/th&gt;                             &lt;th&gt;Buyer Id&lt;\/th&gt;                         &lt;\/Authorized&gt;                     &lt;\/AuthorizeView&gt;                     &lt;th&gt;Status&lt;\/th&gt;                     &lt;th&gt;Products Count&lt;\/th&gt;                     &lt;th&gt;Sum&lt;\/th&gt;                     &lt;th&gt;Address&lt;\/th&gt;                 &lt;\/tr&gt;             &lt;\/thead&gt;             &lt;tbody&gt;                 @if (ViewModel.Model == null)                 {                     &lt;tr&gt;                         &lt;td&gt;                             &lt;em&gt;Loading...&lt;\/em&gt;                         &lt;\/td&gt;                     &lt;\/tr&gt;                 }                 else                 {                     foreach (var order in ViewModel.Model)                     {                          &lt;tr&gt;                             &lt;AuthorizeView Roles=&quot;admin&quot;&gt;                                 &lt;Authorized&gt;                                     &lt;td&gt;@order.Id&lt;\/td&gt;                                     &lt;td&gt;@order.Buyer&lt;\/td&gt;                                 &lt;\/Authorized&gt;                             &lt;\/AuthorizeView&gt;                             &lt;td&gt;@order.Status.ToString(&quot;G&quot;)&lt;\/td&gt;                             &lt;td&gt;@order.Lines.Sum(l =&gt; l.Quantity)&lt;\/td&gt;                             &lt;td&gt;@order.Lines.Sum(l =&gt; l.Quantity * l.Product.Price)&lt;\/td&gt;                             &lt;td&gt;@order.Address&lt;\/td&gt;                         &lt;\/tr&gt;                     }                 }             &lt;\/tbody&gt;         &lt;\/table&gt;     &lt;\/div&gt; }  @functions {      [Parameter]     public string Operation     {         get =&gt; ViewModel.State.ToString(&quot;G&quot;);         set =&gt; ViewModel.ChangeState(value);     }      protected override async Task OnInitializedAsync()     {         await ViewModel.OnInitializedAsync();     } } <\/code><\/pre>\n<p>  <\/p>\n<h2>\u0421\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b<\/h2>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/93\/dw\/2g\/93dw2g_ix7brjvywghcc2fq_54s.png\"><\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/cg\/wo\/kt\/cgwoktslp4sq6epxgumgjqjlj9w.png\"><\/p>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u043d\u0430 Angular 9<\/h2>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/fz\/49\/ur\/fz49urli7r3tfeoasrzfaenii5o.png\"><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/496084\/\"> https:\/\/habr.com\/ru\/post\/496084\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/496084\/\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/j4\/ty\/bj\/j4tybj7jllyqamrgzb-a2wlj5i4.png\"><\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u044e \u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d \u0438 \u0438\u0437\u0443\u0447\u0430\u0442\u044c Blazor. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 \u043d\u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0437\u0430\u043a\u0430\u0437\u0430, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0437\u0430\u043a\u0430\u0437\u043e\u0432 \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043e\u0434\u043d\u043e \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u043e\u0439. \u0417\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u043a\u0430\u0442.   <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-301495","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/301495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=301495"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/301495\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=301495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=301495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=301495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}