ä»æ¥ã¯ãããªãã®ã³ãŒããèšè¿°ããªãããèªåã®æã§ããŒãžããã£ã«ã¿ãªã³ã°ããŠããã²ãŒãããAjaxã°ãªãããã¥ãŒã®äœææ¹æ³ã«ã€ããŠèª¬æããŸãã
ãã®èšäºãæžãå§ãããšããç§ã¯å°ãæ°ãŸããæããŸããããä»ã§ã¯ãã®æ°æã¡ã¯æ¶ããŠããŸããã åé¡ã¯ãã©ã€ãã©ãªèªäœãåçŽã§ãããMVVMãã¿ãŒã³ãåçŽã§ãããåçŽãªããšã説æããããšã§ãã è¿ãå°æ¥ãããã¯ã¢ãŠãã¯ããªãæ®åããã ãããšç¢ºä¿¡ããŠããŸãã ãããŠã1幎ã»ã©åŸã«ããã®èšäºã«åºããã人ããæ瀺ãããè³æã®åçŽãã«å€±æããããšã¯æ¥ããããããšã§ãã 2007 jQueryã®èšäºãéããçããã®ããã«ã
ç³ãç«ãŠããããã¿ã³ã®ã¢ã³ãŒãã£ãªã³ãæããŠããªã人ãããã©ã«ãã®äžã§ããªãã«ãé¡ãããé¡ãããŸãã
äºæ³ã©ããã解決ããå¿ èŠã®ããã¿ã¹ã¯ãèšå®ããŸãããã
ãŠãŒã¶ãŒïŒååãæ§å¥ã幎霢ïŒã®ãªã¹ãã衚瀺ãããããã®ãã©ã¡ãŒã¿ãŒã§æ€çŽ¢ã§ããããã«ããå¿ èŠãããããã³ããšã³ãéçºè ãæ³åããŠãã ããã 人ã®ãªã¹ããããŒãžããšã«è¡šç€ºãããŸãã ãããããã®ããšã¯æ¬¡ã®ããã«ãªããŸãã
ããã¯ãšã³ãå šäœããã§ã«äœæãããŠããããã¹ãŠã®ã€ã³ã¿ãŒãã§ã€ã¹ãæ¢ç¥ã§ãã ããã§ããã«ãããŸã
ActionResult List(FilterParams filterParams, int pageNumber = 1);
ListResultãªããžã§ã¯ãã¯åºåã«è¿ããããæ€çŽ¢çµæãã®é åãšããŒãžãåãæ¿ããããã®ããŒã¿ïŒçŸåšã®ããŒãžçªå·ãšããŒãžæ°ïŒã§æ§æãããŸãã
ã³ãŒãã§ã¯ããã¹ãŠæ¬¡ã®ããã«ãªããŸãã
public class FilterParams { public int? AgeFrom { get; set; } public int? AgeTo { get; set; } public bool ShowMale { get; set; } public bool ShowFemale { get; set; } } public enum Gender { Male, Female } public class PagingData { public int PageNumber { get; set; } public int TotalPagesCount { get; set; } } public class Person { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } public Gender Gender { get; set; } } public class ListResult { IEnumerable<Person> Data { get; set; } PagingData Paging { get; set; } }
ããã§ããã§ã«ã¿ã¹ã¯ã解決ããããšã«éäžã§ããŸãã ããŒã¯ã¢ãããã調æ»ãéå§ããããšãææ¡ããŸãã Knockoutã«ãã§ã«ç²ŸéããŠãã人ã«ãšã£ãŠæ°ããããšã¯äœããããŸãããããã§ã¯ãåå¿è ã«ãšã£ãŠã¯ããã¹ãŠãæ確ã§ããã¹ãã§ãã
<script type="text/html" id="TableRow"> <tr> <td data-bind="text: FirstName"></td> <td data-bind="text: LastName"></td> <td data-bind="text: Gender"></td> <td data-bind="text: Age"></td> </tr> </script> <table> <thead> <tr> <th> First name</th> <th> Last name</th> <th> Gender</th> <th> Age</th> </tr> </thead> <tbody data-bind="template: { name: 'TableRow', foreach: rows }"> </tbody> </table>
ãããã£ãŠã1ã€ã®ã¬ã³ãŒãã®ãã³ãã¬ãŒãããããŸãã ããŒãã«ã«ã¯éçããããŒããããtbodyã¯rowsé åããåã蟌ãŸããŸãã 次ã«ããã®ããŒãã«ã«ããŒã¿ãå ¥åã§ãããã¥ãŒã¢ãã«ãèšè¿°ããå¿ èŠããããŸãã æåã«ã³ãŒãã瀺ãã次ã«èª¬æããŸãã
var viewModel = { rows: ko.observableArray() }; ko.dependentObservable(function () { $.ajax({ url: '/AjaxGrid/List', type: 'POST', context: this, success: function (data) { this.rows(data.Data); } }); }, this); ko.applyBindings(viewModel);
ViewModelã«ã¯1ã€ã®ãã£ãŒã«ãïŒè¡ïŒã®ã¿ãå«ãŸããŸãã æåã¯ç©ºã§ãã 次ã«ãdependentObservableãäœæããŸããããã¯åæåäžã«å®è¡ãããŸãã å®è¡æã«ããµãŒããŒã«AJAXãªã¯ãšã¹ããè¡ããã¬ã¹ãã³ã¹ã®Dataãã£ãŒã«ãã®å€ãrowsãã£ãŒã«ãã«å²ãåœãŠãããŸãã KOã¯ãè¡ãã£ãŒã«ãã®å€æŽã远跡ããå°çãããšã³ããªãããŒãã«ã«è¿œå ããŸãã dependentObservableã®äœæ¥ã®è©³çŽ°ã«ã€ããŠã¯ã å ¬åŒããã¥ã¡ã³ããŸãã¯ãã®ã³ã¡ã³ããåç §ããŠãã ãã ã
次ã®ã¹ãããã¯ãããŒãžåãæ¿ããè¿œå ããããšã§ãã viewModelããå§ããŸããã
var viewModel = { rows: ko.observableArray(), paging: { PageNumber: ko.observable(1), TotalPagesCount: ko.observable(0), next: function () { var pn = this.PageNumber(); if (pn < this.TotalPagesCount()) { this.PageNumber(pn + 1); } }, back: function () { var pn = this.PageNumber(); if (pn > 1) { this.PageNumber(pn - 1); } } } };
ãã®äŸã§ã¯ãMVVMãã¿ãŒã³ã®ViewModelã®æ¬è³ªãéåžžã«ã¯ã£ãããšèŠããŸãã ãã®ã¢ãã«ã¯ã PageNumberãšTotalPagesCountã® 2ã€ã®ããããã£ã§æ§æãããŠããŸã ã ãã®ã¢ãã«ã®è¡šçŸã«ã¯ããã§ã«nextïŒïŒããã³backïŒïŒã¡ãœããããããŸãã isFirstPageãŸãã¯isLastPageããããã£ãå¿ èŠãªå Žåããããã¯viewModelã§ã宣èšãããŸãã ãããã£ãŠãçïŒã¢ãã«ïŒã¯æçšã§å€æŽå¯èœãªåŸè ïŒViewModelïŒã«å²ãŸããŠããŸãã
ããŒãžåãæ¿ãã®è¡šç€ºã¯ç°¡åã§ãã
<script type="text/html" id="PagingPanel"> Page <span data-bind="text: PageNumber" /> of <span data-bind="text: TotalPagesCount" />. <br /> <a href="#next" data-bind="click: back"><</a> <a href="#next" data-bind="click: next">></a> </script> <div data-bind="template: { name: 'PagingPanel', data: paging }"></div>
ãããã£ãŠã衚瀺ãããŠããããŒãžæ°ã®ãã¡ãã©ã®ããŒãžã衚瀺ãããã¿ã³ãååŸã«è¡šç€ºããã ãã§ãã ããŒãžãåãæ¿ãããšãã«ããŒã¿ãæŽæ°ããããã«ã°ãªãããã¥ãŒãæããããšã¯ãã»ãšãã©æ®ã£ãŠããŸããã
ãããè¡ãã«ã¯ãdependentObservableããããã«å€æŽããå¿ èŠããããŸãã
ko.dependentObservable(function () { $.ajax({ url: '/AjaxGrid/List', type: 'POST', data: {pageNumber: this.paging.PageNumber()} context: this, success: function (data) { this.rows(data.Data); } }); }, this);
PageNumberãã£ãŒã«ãã®å€ãAJAXãªã¯ãšã¹ãã«è¿œå ããŸããã ããã§Knockoutã¯ãPageNumberïŒïŒããããã£ãå€æŽããå ŽåãdependentObservableããåã«ãŠã³ããããå¿ èŠãããããšãèªèããŸãã ãããã£ãŠããŠãŒã¶ãŒãããã«ãã¿ã³ãæŒããšãviewModelã¯ãã®ã€ãã³ãããã£ãããïŒdata-bind = "clickïŒnext"ïŒãPageNumberã®å€ã1ã ãå¢ãããŸãã ãã®åŸãKOã¯PageNumberã®å€æŽãçºçããããšã確èªããŸããã€ãŸããdependentObservableããªãŒããŒã©ã€ãããå¿ èŠããããŸãã 次ã«ãAJAXãªã¯ãšã¹ããéä¿¡ããçä¿¡ããŒã¿ãviewModel.rowsã«é 眮ãããŸããããã«ãããããŒãã«ã®ã³ã³ãã³ããå®å šã«åæç»ãããŸãã
次ã«ããã£ã«ã¿ãªã³ã°ãè¿œå ããŸãã ããŒãžåãæ¿ãã«äŒŒãã¢ãããŒãã䜿çšããŸãã ãã¹ãŠã®æ€çŽ¢ãã©ã¡ãŒã¿ãŒã¯èŠ³å¯å¯èœã§ããããã®å€ã¯ãªã¯ãšã¹ããéä¿¡ããããšãã«éä¿¡ãããŸãã ã€ãŸã ãã£ã«ã¿ãªã³ã°æ¡ä»¶ãå€æŽããããšããªã¯ãšã¹ãããµãŒããŒã«éä¿¡ãããŸãã
var viewModel = { filterParams: { ShowMale: ko.observable(true), ShowFemale: ko.observable(true), AgeFrom: ko.observable(), AgeTo: ko.observable() }, rows: ko.observableArray(), paging: { PageNumber: ko.observable(1), TotalPagesCount: ko.observable(0), next: function () { var pn = this.PageNumber(); if (pn < this.TotalPagesCount()) { this.PageNumber(pn + 1); } }, back: function () { var pn = this.PageNumber(); if (pn > 1) { this.PageNumber(pn - 1); } } } };
å®éã®ãã£ã«ã¿ãŒããã«ãã¥ãŒïŒ
<script type="text/html" id="FiltrationPanel"> Age from <input type="text" size="3" data-bind="value: AgeFrom" /> to <input type="text" size="3" data-bind="value: AgeTo" /> <br /> <label><input type="checkbox" data-bind="checked: ShowMale" />Show male</label> <br /> <label><input type="checkbox" data-bind="checked: ShowFemale" />Show female</label> </script> <div data-bind="template: { name: 'FiltrationPanel', data: filterParams }"></div>
ãããŠdependentObservableããããã«çžé¢ãããå¿ èŠããããŸãã
ko.dependentObservable(function () { var data = ko.utils.unwrapObservable(this.filterParams); // Dependent observable will react only on page number change. data.pageNumber = this.paging.PageNumber(); $.ajax({ url: url, type: 'POST', data: data, context: this, success: function (data) { this.rows(data.Data); this.paging.PageNumber(data.Paging.PageNumber); this.paging.TotalPagesCount(data.Paging.TotalPagesCount); } }); }, this); ko.dependentObservable(function () { var data = ko.toJS(this.filterParams); // Reset page number when any filtration parameters change this.paging.PageNumber(1); }, this);
å®éãåãæ¶ãç·ã®æ®µèœã§ã¯ããã£ã«ã¿ãŒæ¡ä»¶ãå€æŽããããšãã«2ã€ã®ãµãŒããŒãªã¯ãšã¹ãã«ã€ãªãããœãªã¥ãŒã·ã§ã³ã«ã€ããŠèª¬æããŸããã 1ã€ã¯å€æŽèªäœãåå ã§ã2ã€ç®ã¯pageNumberã1ã«èšå®ããããšã«ãããã®ã§ãã ç¶æ³ãä¿®æ£ããããã«ã次ã®è¡ãä¿®æ£ããŸãã
var data = ko.toJS(this.filterParams);
ã«
var data = ko.utils.unwrapObservable(this.filterParams);
ãã®å Žåã unwrapObservableã¯toJSã¡ãœãããšåãçµæãè¿ããŸããã filterParamsãå€æŽããããšdependentObservableãåã«ãŠã³ããããŸãã
ãããã£ãŠããµãŒããŒãžã®èŠæ±ã¯ããŒãžçªå·ã®å€æŽã®ã¿ãéå§ããããŒãžã®åãæ¿ããŸãã¯ãã£ã«ã¿ãªã³ã°æ¡ä»¶ã®å€æŽã«ãã£ãŠåŒãèµ·ããããå¯èœæ§ããããŸãã
ãŸãããµãŒããŒããå¿çãåä¿¡ãããšãããŒãžæ°ãŸãã¯çŸåšã®ããŒãžã®æ°ãå€æŽãããå Žåã«ããŒãžã³ã°ãã£ãŒã«ãã®å€ãæŽæ°ããŸãïŒããšãã°ãããŒãž10ããªã¯ãšã¹ããã5ã€ãããããŸããïŒã
å®éãç§ãã¡ã¯ãã§ã«èªåèªèº«ã®ã¿ã¹ã¯ã»ããã解決ããŠããŸãã ããããç§ã¯åœŒå¥³ããå°ãèŠçŽãææ¡ããèããŸãã éåžžã«å ·äœçãªåé¡ã解決ããŸããã ããããViewModelã¯ã¿ã¹ã¯èªäœã«ã€ããŠã»ãšãã©ç¥ããŸããã 圌女ã¯URLãããŒã¿ã®ååŸå ããã£ã«ã¿ãªã³ã°ãªãã·ã§ã³ã«ã€ããŠã®ã¿ç¥ã£ãŠããŸãã ããã ãã§ã ããã¯ãã³ãŒããåå©çšå¯èœã«ã§ããããšãæå³ããŸãã viewModelããurlåŒæ°ãšfilteringParamsåŒæ°ãæã€ã¯ã©ã¹ã«å€æããŸãã
var AjaxGridViewModel = function(url, filterParams) { this.rows= ko.observableArray(); this.filterParams = filterParams; this.paging = { PageNumber: ko.observable(1), TotalPagesCount: ko.observable(0), next: function () { var pn = this.PageNumber(); if (pn < this.TotalPagesCount()) this.PageNumber(pn + 1); }, back: function () { var pn = this.PageNumber(); if (pn > 1) this.PageNumber(pn - 1); } }; ko.dependentObservable(function () { var data = ko.utils.unwrapObservable(this.filterParams); // Dependent observable will react only on page number change. data.pageNumber = this.paging.PageNumber(); $.ajax({ url: url, type: 'POST', data: data, context: this, success: function (data) { this.rows(data.Data); this.paging.PageNumber(data.Paging.PageNumber); this.paging.TotalPagesCount(data.Paging.TotalPagesCount); } }); }, this); ko.dependentObservable(function () { var data = ko.toJS(this.filterParams); // Reset page number when any filtration parameters change this.paging.PageNumber(1); }, this); };
ãã®ã³ãŒãã¯ãã¹ãŠæ£ç¢ºã«39è¡ãå æããŸãã ã¿ã€ãã«ãæãåºããšãåæåçšã®ã¿ã€ãã«ãæ®ã£ãŠããŸãã
ko.applyBindings(new AjaxGridViewModel('/Ajax/List', { ShowMale: ko.observable(true), ShowFemale: ko.observable(true), AgeFrom: ko.observable(), AgeTo: ko.observable() });
ã芧ã®ãšããã2çªç®ã®åŒæ°ã¯å šäœåãå°ç¡ãã«ããŸãã ãªããžã§ã¯ãã1è¡ã§èšè¿°ãã代ããã«ããã®æ§è³ªã«ã€ããŠèããŠã¿ãŸãããã å®éãããã¯CïŒã§èª¬æãããŠããFilterParamsãªããžã§ã¯ãã®ã³ããŒã¢ã³ãããŒã¹ãã§ãã ãã®ãã£ãŒã«ãã¯Viewã§ã®ã¿äœ¿çšãããViewModelã§ã¯æããã«ããããæ瀺çã«äœ¿çšããŸããã ããã«ãããViewModelãããã®ã¯ã©ã¹ãåãããšãã§ããŸãã
ãã®äŸã§ã¯ãASP.NET MVCã䜿çšããŸããã ãããŠãç§ã¯ãã®åé¡ãéåžžã«ç°¡åã«è§£æ±ºããŸããïŒ
C#: public ActionResult Index() { return View(new FilterParams()); } CSHTML: ko.applyBindings(new AjaxGridViewModel('@Url.Action("List")', @Html.ToJSON(Model)))
ã€ãŸããããã©ã«ãã®ãã£ã«ã¿ãªã³ã°èšå®ãæã€ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ããã¥ãŒã«æž¡ãã ãã§ããã¥ãŒã¯ãããçŽååããŠJSãªããžã§ã¯ãã«å€æããŸãã ãããã£ãŠãã³ãŒãã®ã¡ã³ããã³ã¹äœæ¥ãç°¡çŽ åããŸããã ãã®ãªããžã§ã¯ãã䜿çšãããæªã³ã³ãã€ã«ã®å Žæã¯ããã³ãã¬ãŒãFiltrationPanelã ãã§ãã
ããããããã ãã§ã¯ãããŸããã åœåã filtrationParamsãã£ãŒã«ãã«ã¯èŠ³æž¬å¯èœãªå€ãå«ãŸããŠããŸããã ãããŠä»ãç§ãã¡ã¯åœŒã«åçŽãªJSãªããžã§ã¯ããäžããŸããã ãã®ãªããžã§ã¯ãã®ãã¹ãŠã®ãã£ãŒã«ããko.observableïŒïŒã§ã©ããããå¿ èŠããããŸãã ãã®ããã®ko.mappingãã©ã°ã€ã³ããããŸãã
AjaxGridViewModelã¯ã©ã¹ã®2è¡ç®ã§ãã®ãã©ã°ã€ã³ã䜿çšããŸãã
var AjaxGridViewModel = function(url, filterParams) { this.rows= ko.observableArray(); this.filterParams = ko.mapping.fromJS(filterParams); ...
確ãã«ããã ãã§ãã
ãããŠä»ãjqGridãªã©ãããã®ã«ãªããããå¿ èŠãªã®ãã äžçªäžã®è¡ã¯ããããã¯ãã¹ãŠè¡šã衚瀺ããããã«é©å¿ãããééã³ã³ãããŒã«ã§ãã 圌ãã«ã¯å€ãã®æ©äŒããããŸããããããã¯éåžžã«çãç¯å²ã察象ãšããŠããŸãã ãããŠãåå©çšå¯èœãªviewModelãšçµ¶å¯Ÿçã«è»œéãªãã¥ãŒãäœæããŸããã ããŒãã«ããªã¹ããªã©äœã§ã䜿çšã§ããŸãã ãã®å Žåã衚瀺ãããããŒã¿ã¯ãµãŒããŒã³ãŒããšhtmlã®ã¿ãç¥ã£ãŠããŸãã ãããŠãããæè»æ§ã§ãã ããŒãžã®ãã£ã«ã¿ãªã³ã°ãšãªãŒãã§ããŒã¿ã衚瀺ããããã®äŸ¿å©ãªããŒã«ãæã«å ¥ããŸããã ã³ãŒãã¯å°ãããã©ã®ããã«æ©èœããããå®å šã«ç解ããŠããŸãã ãããã§ããã
èšäºããã¹ã¿ãŒãããã¹ãŠã®äººã«æè¬ããŸãã ãããããããããŠäŸ¿å©ã ã£ãããšãé¡ã£ãŠããŸãã ãã®ãªã³ã¯ããASP.NET MVC 3ã®ãµã³ãã«ã®æçµããŒãžã§ã³ã®ãœãŒã¹ã³ãŒããããŠã³ããŒãã§ããŸãã
ãæž èŽããããšãããããŸããã ç§ã¯è³ªåãšå»ºèšçãªæ¹å€ã«åãã§ããã§ãããã
æŽæ° ïŒãã£ã«ã¿ãŒæ¡ä»¶ãå€æŽãããšãã®2ã€ã®ãµãŒããŒèŠæ±ã«é¢ããåé¡ãä¿®æ£ããŸããã