Fungsionalitas kode minimum VueJs + MVC

Selamat siang


Saya menggunakan WPF selama bertahun-tahun. Pola MVVM mungkin merupakan salah satu pola arsitektur yang paling nyaman. Saya berasumsi bahwa MVC hampir sama. Ketika saya melihat penggunaan MVC dalam praktek di tempat kerja yang baru, saya terkejut dengan kerumitan dan pada saat yang sama kurangnya kegunaan dasar. Yang paling menjengkelkan adalah bahwa validasi hanya terjadi ketika formulir kelebihan beban. Tidak ada bingkai merah yang menyoroti bidang kesalahan, tetapi hanya peringatan dengan daftar kesalahan. Jika ada banyak kesalahan, maka Anda harus memperbaiki beberapa kesalahan dan menyimpan untuk menyimpan untuk mengulangi validasi. Tombol simpan selalu aktif. Daftar tertaut benar-benar diimplementasikan melalui js, tetapi rumit dan membingungkan. Model, tampilan, dan pengontrol tergabung dengan erat jadi ujilah semuanya keindahan sangat sulit.
Bagaimana menghadapi ini ?? Kepada siapa itu menarik saya bertanya di bawah kat.


Jadi kita punya:
Membangun formulir MVC dalam bentuk klasik tidak menyiratkan cara lain untuk berinteraksi dengan server sebagai membebani seluruh halaman, yang tidak nyaman bagi pengguna.
Penggunaan penuh kerangka kerja seperti Reart, Angular, Vue dan transisi ke SinglePageApplicatrion akan membuat antarmuka yang lebih nyaman, tetapi sayangnya, pada prinsipnya, itu tidak mungkin dalam kerangka proyek ini karena:
-Banyak kode telah ditulis, diterima, dan tidak ada yang akan membiarkan Anda mengulanginya.
-Kami adalah programmer C # dan tidak tahu js dalam jumlah yang tepat.


Selain itu, kerangka kerja Reart, Angular, Vue dipertajam untuk menulis logika kompleks pada klien, yang dalam tampilan WPF saya tidak benar. Semua logika harus di satu tempat dan ini adalah objek bisnis dan / atau kelas model. Lihat seharusnya hanya menampilkan keadaan model tidak lebih.
Berdasarkan hal tersebut di atas, saya mencoba untuk menemukan pendekatan yang memungkinkan Anda untuk mendapatkan fungsionalitas maksimum dengan minimum kode js. Pertama-tama, kode minimum yang perlu ditulis untuk menghasilkan dan memperbarui bidang tertentu.
Bundel VueJs + MVC yang saya usulkan terlihat seperti ini:


  • VueJs digunakan dalam versi paling sederhana dengan koneksi via cdn. Jika diperlukan komponen komponen yang sama dapat dihubungkan melalui cdn.
  • Setelah memuat, Vue memuat data formulir melalui Ajax.
  • Setiap kali formulir berubah, Vue mengirim semua perubahan ke server (untuk bidang teks, Anda dapat mengonfigurasi bahwa perubahan dikirim saat fokus hilang).
  • Validasi terjadi di server melalui mekanisme Entity dan bidang yang tidak valid dikembalikan ke klien dan tanda bahwa keadaan model telah berubah sehubungan dengan database.
    -Jika permintaan validasi berikutnya terjadi lebih awal dari yang sebelumnya, permintaan validasi sebelumnya akan dibatalkan.
    Model MVC tidak digunakan. Fungsi ViewModel dalam arti WPF kabur di sini antara vue dan controller.
    Keuntungan dari implementasi seperti itu di atas halaman Razor klasik:
  • Antarmuka digambar menggunakan alat Vue, yang dirancang untuk menggambar antarmuka. Keuntungan utama.
  • Memisahkan layer View dari ViewModel.
  • kesalahan validasi ditampilkan saat formulir terisi.
  • pengujian kenyamanan
    Kekurangan:
  • Memuat berlebihan di server dengan permintaan validasi.
  • Kebutuhan untuk mengetahui vue dan js seminimal mungkin.


    Saya menganggap pendekatan ini sebagai templat awal untuk bekerja dengan formulir.
    Dalam aplikasi nyata untuk formulir tertentu, diinginkan untuk mengoptimalkan:
    1) Kirim permintaan validasi hanya ketika mengubah bidang, yang harus divalidasi di server.
    2) Validasi panjang, bidang penuh, dll. berjalan di klien.



Jadi ayo pergi.


Dalam contoh saya, saya menggunakan database pelatihan Northwind, yang saya unduh dengan salah satu contoh Devextreem.
Pembuatan aplikasi, koneksi Entitas dan pembuatan DbContext Saya akan meninggalkan layar. Tautkan ke github dengan contoh di akhir artikel.
Buat pengontrol MVC 5. kosong yang baru. Sebut saja OrdersController. Sejauh ini ada satu metode.


public ActionResult Index() { return View(); } 

Tambahkan satu lagi


  public ActionResult Edit() { return View(); } 

Sekarang Anda perlu pergi ke folder Views / Orders dan menambahkan dua halaman Index.cshtml dan Edit.cshtml
Catatan penting bahwa halaman cshtml berfungsi tanpa model harus ditambahkan ke bagian atas halaman System.Web.Mvc.WebViewPage.
Diasumsikan bahwa Index.cshtml berisi tabel dari mana garis yang disorot akan pergi ke halaman edit. Untuk saat ini, buat saja tautan yang akan mengarah ke halaman edit.


 @inherits System.Web.Mvc.WebViewPage <table > @foreach (var item in ViewBag.Orders) { <tr><td><a href="Edit?id=@item.OrderID">@item.OrderID</a></td></tr> } </table> 

Sekarang saya ingin mengimplementasikan pengeditan objek yang ada.


Hal pertama yang harus dilakukan adalah mendeskripsikan metode di controller yang akan mengembalikan deskripsi objek ke klien Json dengan pengidentifikasi.


  [HttpGet] public ActionResult GetById(int id) { var order = _db.Orders.Find(id);//  string orderStr = JsonConvert.SerializeObject(order);//  return Content(orderStr, "application/json");// } 

Anda dapat memverifikasi bahwa semuanya berfungsi dengan mengetikkan browser (nomor port secara alami milik Anda) http: // localhost: 63164 / Pesanan / GetById? Id = 10501
Anda harus mendapatkan sesuatu seperti ini di browser


 { "OrderID": 10501, "CustomerID": "BLAUS", "EmployeeID": 9, "OrderDate": "1997-04-09T00:00:00", "RequiredDate": "1997-05-07T00:00:00", "ShippedDate": "1997-04-16T00:00:00", "ShipVia": 3, "Freight": 8.85, "ShipName": "Blauer See Delikatessen", "ShipAddress": "Forsterstr. 57", "ShipCity": "Mannheim", "ShipRegion": null, "ShipPostalCode": "68306", "ShipCountry": "Germany" } 

Baik dan (atau) menulis tes sederhana. Namun, mari tinggalkan pengujian di luar cakupan artikel ini.


  [Test] public void OrderControllerGetByIdTest() { var bdContext = new Northwind(); var id = bdContext.Orders.First().OrderID; //    var orderController = new OrdersController(); var json = orderController.GetById(id) as ContentResult; var res = JsonConvert.DeserializeObject(json.Content,typeof(Order)) as Order; Assert.AreEqual(id, res.OrderID); } 

Selanjutnya, Anda perlu membuat formulir Vue.


 @inherits System.Web.Mvc.WebViewPage <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> </title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <h1>A  </h1> <table > <tr v-for="(item,i) in order"> @*      *@ <td> {{i}}</td> <td> <input type="text" v-model="order[i]"/> </td> </tr> </table> </div> <script> new Vue({ el: "#app", data: { order: { OrderID: 10501, CustomerID: "BLAUS", EmployeeID: 9, OrderDate: "1997-04-09T00:00:00", RequiredDate: "1997-05-07T00:00:00", ShippedDate: "1997-04-16T00:00:00", ShipVia: 3, Freight: 8.85, ShipName: "Blauer See Delikatessen", ShipAddress: "Forsterstr. 57", ShipCity: "Mannheim", ShipRegion: null, ShipPostalCode: "68306", ShipCountry: "Germany" } } }); </script> </body> </html> 

Jika semuanya dilakukan dengan benar, maka prototipe formulir yang akan datang harus ditampilkan di browser.



Seperti yang bisa kita lihat, Vue menampilkan semua bidang persis seperti modelnya. Tetapi data dalam model masih statis dan hal pertama yang harus dilakukan selanjutnya adalah mengimplementasikan memuat data dari database melalui metode yang baru saja ditulis.
Untuk melakukan ini, tambahkan metode fetchOrder () dan panggil di bagian mount:


  new Vue({ el: "#app", data: { id: @ViewBag.Id, order: { OrderID: 0, CustomerID: "", EmployeeID: 0, OrderDate: "", RequiredDate: "", ShippedDate: "", ShipVia: 0, Freight: 0, ShipName: "0", ShipAddress: "", ShipCity: "", ShipRegion: null, ShipPostalCode: "", ShipCountry: "" }, }, methods: { //  fetchOrder() { var path = "../Orders/GetById?key=" + this.id; console.log(path); this.fetchJson(path, json => this.order = json); }, //    fetch fetchJson(path, collback) { try { fetch(path, { mode: 'cors' }) .then(response => response.json()) .then(function(json) { collback(json); } ); } catch (ex) { alert(ex); } } }, mounted: function() { this.fetchOrder(); } }); 

Nah, karena pengidentifikasi objek sekarang harus berasal dari controller, maka di controller Anda harus melewati pengidentifikasi ke objek ViewBag dinamis, sehingga dapat diperoleh dalam View.


  public ActionResult SimpleEdit(int id = 0) { ViewBag.Id = id; return View(); } 

Ini cukup untuk membaca data saat boot.
Saatnya untuk menyesuaikan formulir.
Agar tidak membebani artikel, saya menyimpulkan minimal bidang. Saya menyarankan pemula untuk mencari cara bekerja dengan daftar tertaut.


  <table > <tr> <td> </td> <td > <input type="number" v-model="order.Freight" /> </td> </tr> <tr> <td>  </td> <td> <input type="text" v-model="order.ShipCountry" /> </td> </tr> <tr> <td> </td> <td> <input type="text" v-model="order.ShipCity" /> </td> </tr> <tr> <td> </td> <td> <input type="text" v-model="order.ShipAddress" /> </td> </tr> </table> 

Bidang ShipCountry dan ShipAddress adalah kandidat terbaik untuk daftar tertaut.
Berikut adalah metode pengontrolnya. Seperti yang Anda lihat, semuanya sangat sederhana. Semua penyaringan dilakukan menggunakan Linq.


  /// <summary> ///    c     ///       ,    /// </summary> /// <param name="country"></param> /// <param name="region"></param> /// <returns></returns> [HttpGet] public ActionResult AvaiableCityList( string country,string region=null) { var avaiableCity = _db.Orders.Where(c => ((c.ShipRegion == region) || region == null)&& (c.ShipCountry == country) || country == null).Select(a => a.ShipCity).Distinct(); var jsonStr = JsonConvert.SerializeObject(avaiableCity); return Content(jsonStr, "application/json"); } /// <summary> ///    c   ///    ,    /// </summary> /// <param name="region"></param> /// <returns></returns> [HttpGet] public ActionResult AvaiableCountrys(string region=null) { var resList = _db.Orders.Where(c => (c.ShipRegion == region)||region==null).Select(c => c.ShipCountry).Distinct(); var json = JsonConvert.SerializeObject(resList); return Content(json, "application/json"); } 

Namun dalam kode Lihat telah ditambahkan secara signifikan lebih banyak.
Selain fungsi negara dan kota, Anda harus menambahkan arloji yang memantau perubahan objek, sayangnya nilai lama dari objek vue yang kompleks tidak disimpan, jadi Anda harus menyimpannya secara manual, yang saya gunakan dengan metode saveOldOrderValue: sementara saya hanya menyimpan negara di dalamnya. Ini memungkinkan Anda membaca kembali daftar kota hanya ketika negara berubah. Kalau tidak, kodenya sama, saya pikir. Dalam contoh ini, saya hanya menunjukkan daftar tertaut satu tingkat (dengan prinsip ini tidak sulit untuk membuat sarang dari tingkat mana pun).


 @inherits System.Web.Mvc.WebViewPage <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> </title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <table> <tr> <td>C </td> <td> <input type="number" v-model="order.Freight" /> </td> </tr> <tr> <td>  </td> <td> <select v-model="order.ShipCountry" class="input"> <option v-for="item in AvaialbeCountrys" :v-key="item">{{item}} </option> </select> </td> </tr> <tr> <td> </td> <td> <select v-model="order.ShipCity" > <option v-for="city in AvaialbeCitys" :v-key="city">{{city}} </option> </select> </td> </tr> <tr> <td> </td> <td> <input type="text" v-model="order.ShipAddress" /> </td> </tr> </table> </div> <script> new Vue({ el: "#app", data: { id: @ViewBag.Id, order: { OrderID: 0, CustomerID: "", EmployeeID: 0, OrderDate: "", RequiredDate: "", ShippedDate: "", ShipVia: 0, Freight: 0, ShipName: "0", ShipAddress: "", ShipCity: "", ShipRegion: null, ShipPostalCode: "", ShipCountry: "" }, oldOrder: { ShipCountry: "" }, AvaialbeCitys: [], AvaialbeCountrys: [] }, methods: { //  fetchOrder() { var path = "../Orders/GetById?Id=" + this.id; this.fetchJson(path, json => this.order = json); }, fetchCityList() { //     var country = this.order.ShipCountry; if (country == null || country === "") { country = ''; } var path = "../Orders/AvaiableCityList?country=" + country; this.fetchJson(path, json => {this.AvaialbeCitys = json;}); }, fetchCountrys() { var path = "../Orders/AvaiableCountrys"; this.fetchJson(path,jsonResult => {this.AvaialbeCountrys = jsonResult;}); }, //    fetch fetchJson(path, collback) { try { fetch(path, { mode: 'cors' }) .then(response => response.json()) .then(function(json) { collback(json); } ); } catch (ex) { alert(ex); } }, saveOldOrderValue:function(){ this.oldOrder.ShipCountry = this.order.ShipCountry; } }, watch: { order: { handler: function (after) { if (this.oldOrder.ShipCountry !== after.ShipCountry)//    { this.fetchCityList();//       } this.saveOldOrderValue(); }, deep: true } }, mounted: function () { this.fetchCountrys();//   //    ,      this.fetchOrder();//  this.saveOldOrderValue();//   } }); </script> </body> </html> 

Topik terpisah adalah Validasi. Dari sudut pandang optimasi kecepatan, tentu saja, Anda perlu melakukan validasi pada klien. Tetapi ini akan menyebabkan duplikasi kode, jadi saya menunjukkan contoh dengan validasi pada tingkat Entity (Seperti seharusnya idealnya). Pada saat yang sama, kode minimum, validasi itu sendiri terjadi dengan cukup cepat dan juga tidak sinkron. Seperti yang telah ditunjukkan oleh praktik, bahkan dengan Internet yang sangat lambat, semuanya berfungsi lebih dari biasanya.
Masalah hanya muncul jika teks diketik dengan cepat di bidang teks, dan kecepatan pengetikan adalah 260 karakter per menit. Opsi pengoptimalan paling sederhana untuk bidang teks adalah mengatur pembaruan malas-model v. Lazy = " order.ShipAddress ", maka validasi akan terjadi ketika fokus berubah. Opsi yang lebih lanjut adalah membuat penundaan validasi untuk bidang ini + jika permintaan validasi berikutnya dipanggil sebelum menerima respons, lalu abaikan pemrosesan permintaan sebelumnya.
Metode untuk memproses validasi dalam kontrol adalah sebagai berikut.


  [HttpGet] public ActionResult Validate(int id, string json) { var order = _db.Orders.Find(id); JsonConvert.PopulateObject(json, order); var errorsD = GetErrorsJsArrey(); return Content(errorsD.ToString(), "application/json"); } private String GetErrorsAndChanged() { var changed= _db.ChangeTracker.HasChanges(); var errors = _db.GetValidationErrors(); return GetErrorsAndChanged(errors,changed); } private static string GetErrorsAndChanged(IEnumerable<DbEntityValidationResult> errors,bool changed) { dynamic dynamic = new ExpandoObject(); dynamic.IsChanged = changed;//  IsChanged var errProperty = new Dictionary<string, object>();//      dynamic.Errors = new DynObject(errProperty);//        foreach (DbEntityValidationResult validationError in errors)//   { foreach (DbValidationError err in validationError.ValidationErrors)//   { errProperty.Add(err.PropertyName,err.ErrorMessage); } } var json = JsonConvert.SerializeObject(dynamic); return json; } 

     DynObject 

  public sealed class DynObject : DynamicObject { private readonly Dictionary<string, object> _properties; public DynObject(Dictionary<string, object> properties) { _properties = properties; } public override IEnumerable<string> GetDynamicMemberNames() { return _properties.Keys; } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (_properties.ContainsKey(binder.Name)) { result = _properties[binder.Name]; return true; } else { result = null; return false; } } public override bool TrySetMember(SetMemberBinder binder, object value) { if (_properties.ContainsKey(binder.Name)) { _properties[binder.Name] = value; return true; } else { return false; } } } 

Cukup verbose, tetapi kode ini ditulis sekali untuk seluruh aplikasi dan tidak memerlukan penyetelan untuk objek atau bidang tertentu. Sebagai hasil dari metode yang bekerja pada klien, objek json dengan properti IsChanded dan Kesalahan. Properti ini secara alami perlu dibuat di Vue kami dan diisi dengan setiap perubahan objek.
Untuk mendapatkan kesalahan validasi, Anda perlu mengatur validasi ini di suatu tempat. Saatnya menambahkan beberapa atribut validasi ke deskripsi kami tentang Entitas objek Pesanan.


  [MinLength(10)] [StringLength(60)] public string ShipAddress { get; set; } [CheckCityAttribute(" ShipCity   ")] public string ShipCity { get; set; } 

MinLength dan StringLength adalah atribut standar, tetapi untuk ShipCity saya membuat atribut khusus


  /// <summary> /// Custom Attribute Example /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class CheckCityAttribute : ValidationAttribute { public CheckCityAttribute(string message) { this.ErrorMessage = message; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { ValidationResult result = ValidationResult.Success; string[] memberNames = new string[] { validationContext.MemberName }; string val = value?.ToString(); Northwind _db = new Northwind(); Order order = (Order)validationContext.ObjectInstance; bool exsist = _db.Orders.FirstOrDefault(o => o.ShipCity == val && o.ShipCountry == order.ShipCountry)!=null; if (!exsist) { result = new ValidationResult(string.Format(this.ErrorMessage,order.ShipCity , val), memberNames); } return result; } } 

Namun, mari kita tinggalkan topik validasi Entitas juga di luar ruang lingkup artikel ini.
Untuk menampilkan kesalahan, Anda perlu menambahkan tautan ke Css dan sedikit memodifikasi formulir.
Beginilah tampilan formulir yang diubah sekarang:


 @inherits System.Web.Mvc.WebViewPage <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title> id=@ViewBag.Id</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <link rel="stylesheet" type="text/css" href="~/Content/vueError.css" /> </head> <body> <div id="app"> <table> <tr> <td> </td> <td class="tooltip"> <input type="number" v-model="order.Freight" v-bind:class="{error:!errors.Freight==''} " class="input" /> <span v-if="errors.Freight!==''" class="tooltiptext">{{errors.Freight}}</span> </td> </tr> <tr> <td>  </td> <td> <select v-model="order.ShipCountry" class="input"> <option v-for="item in AvaialbeCountrys" :v-key="item">{{item}} </option> </select> </td> </tr> <tr> <td> </td> <td class="tooltip"> <select v-model="order.ShipCity" v-bind:class="{error:!errors.ShipCity==''}" class="input"> <option v-for="city in AvaialbeCitys" :v-key="city">{{city}} </option> </select> <span v-if="!errors.ShipCity==''" class="tooltiptext">{{errors.ShipCity}}</span> </td> </tr> <tr> <td> </td> <td class="tooltip"> <input type="text" v-model.lazy="order.ShipAddress" v-bind:class="{error:!errors.ShipAddress=='' }" class="input" /> <span v-if="!errors.ShipAddress==''" class="tooltiptext">{{errors.ShipAddress}}</span> </td> </tr> <tr> <td> </td> <td> <button v-on:click="Save()" :disabled="IsChanged===false" || hasError class="alignRight">Save</button> </td> </tr> </table> </div> <script> new Vue({ el: "#app", data: { id: @ViewBag.Id, order: { OrderID: 0, CustomerID: "", EmployeeID: 0, OrderDate: "", RequiredDate: "", ShippedDate: "", ShipVia: 0, Freight: 0, ShipName: "0", ShipAddress: "", ShipCity: "", ShipRegion: null, ShipPostalCode: "", ShipCountry: "" }, oldOrder: { ShipCountry: "" }, errors: { OrderID: null, CustomerID: null, EmployeeID: null, OrderDate: null, RequiredDate: null, ShippedDate: null, ShipVia: null, Freight: null, ShipName: null, ShipAddress: null, ShipCity: null, ShipRegion: null, ShipPostalCode: null, ShipCountry: null }, IsChanged: false, AvaialbeCitys: [], AvaialbeCountrys: [] }, computed : { hasError: function () { for (var err in this.errors) { var error = this.errors[err]; if (error !== '' || null) return true; } return false; } }, methods: { //  fetchOrder() { var path = "../Orders/GetById?Id=" + this.id; this.fetchJson(path, json => this.order = json); }, fetchCityList() { //     var country = this.order.ShipCountry; if (country == null || country === "") { country = ''; } var path = "../Orders/AvaiableCityList?country=" + country; this.fetchJson(path, json => {this.AvaialbeCitys = json;}); }, fetchCountrys() { var path = "../Orders/AvaiableCountrys"; this.fetchJson(path,jsonResult => {this.AvaialbeCountrys = jsonResult;}); }, //    fetch Validate() {this.Action("Validate");}, Save() {this.Action("Save");}, Action(action) { var myJSON = JSON.stringify(this.order); var path = "../Orders/" + action + "?id=" + this.id + "&json=" + myJSON; this.fetchJson(path, jsonResult => { this.errors = jsonResult.Errors; this.IsChanged = jsonResult.IsChanged; }); }, fetchJson(path, collback) { try { fetch(path, { mode: 'cors' }) .then(response => response.json()) .then(function(json) { collback(json); } ); } catch (ex) { alert(ex); } }, saveOldOrderValue:function(){ this.oldOrder.ShipCountry = this.order.ShipCountry; } }, watch: { order: { handler: function (after) { this.IsChanged=true; if (this.oldOrder.ShipCountry !== after.ShipCountry)//    { this.fetchCityList();//       } this.saveOldOrderValue(); this.Validate(); }, deep: true } }, mounted: function () { this.fetchCountrys();//   //    ,      this.fetchOrder();//  this.saveOldOrderValue();//   } }); </script> </body> </html> 

Sepertinya CSS


 .tooltip { position: relative; display: inline-block; border-bottom: 1px dotted black; } .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: #555; color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -60px; opacity: 0; transition: opacity 0.3s; } .tooltip .tooltiptext::after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: #555 transparent transparent transparent; } .tooltip:hover .tooltiptext { visibility: visible; opacity: 1; } .error { color: red; border-color: red; border-style: double; } .input { width: 200px ; } .alignRight { float: right } 

Dan inilah hasil pekerjaannya.



Untuk memahami cara kerja validasi, mari kita hati-hati melihat markup yang menjelaskan satu bidang:


 <td class="tooltip"> <input type="number" **v-model="order.Freight" v-bind:class="{error:!errors.Freight==''} " **class="input" /> <span v-if="errors.Freight!==''" class="tooltiptext">{{errors.Freight}}</span> </td> 

Berikut adalah 2 poin penting:


Bagian markup ini menghubungkan gaya yang bertanggung jawab untuk perbatasan merah di sekitar elemen v-bind: class = "{error :! Errors.Freight == ''} di sini vue menghubungkan kelas css dengan syarat.


Dan di sini adalah jendela sembul yang diperlihatkan saat kursor mouse melewati elemen:


  <span v-if="errors.Freight!==''" class="tooltiptext">{{errors.Freight}}</span> 

selain itu, elemen induk elemen harus mengandung atribut class = "tooltip".


Pada versi terakhir, tombol simpan ditambahkan dikonfigurasi sehingga hanya akan tersedia jika penyimpanan dimungkinkan.
Untuk menyederhanakan markup yang diperlukan untuk validasi, saya mengusulkan untuk menulis komponen paling sederhana yang akan mengambil semua validasi itu sendiri.


 Vue.component('error-aborder', { props: { error: String }, template: '<div class="tooltip" >' + '<div v-bind:class="{error:!error==\'\' }" >' + '<slot>test</slot>' + '</div>' + '<p class="tooltiptext" v-if="!error==\'\'" >{{error}}</p>' + '</div>' }); 

sekarang markupnya terlihat lebih rapi.


  <error-aborder v-bind:error="errors.Freight"> <input type="number" v-model="order.Freight" class="input" /> </error-aborder> 

Pengembangan datang ke mengatur bidang pada formulir, mengatur validasi di Entyty, dan membuat daftar. Jika daftar itu statis dan tidak besar, maka mereka dapat sepenuhnya diatur dalam kode.


Bagian C # dari kode diuji dengan baik. Paket yang akan datang berurusan dengan pengujian Vue.


Hanya itu yang ingin saya sampaikan.
Saya akan sangat menghargai kritik yang membangun.


Berikut ini tautan ke kode sumber .


Dalam contoh, formulir ini disebut SimpleEdit dan berisi versi terbaru. Siapa pun yang tertarik dengan opsi pendahuluan dapat melalui komit.
Dalam contoh, saya menerapkan pengoptimalan: membatalkan permintaan validasi jika, tanpa menunggu respons validasi, menyebabkan validasi untuk kedua kalinya.

Source: https://habr.com/ru/post/id455367/


All Articles