ã¿ãªããããã«ã¡ã¯ã
éçºã§ReactJSã䜿çšãå§ããŠãã1幎以äžãçµéããŸããã æåŸã«ãç§ãã¡ã®äŒç€Ÿãã©ãã»ã©å¹žãã«ãªã£ãããå
±æããæãæ¥ãŸããã ãã®èšäºã§ã¯ããã®ã©ã€ãã©ãªã䜿çšããããã«ãªã£ãçç±ãšãã®æ¹æ³ã«ã€ããŠèª¬æããŸãã
ãªããããã¹ãŠ
ç§ãã¡ã¯å°ããªäŒç€Ÿã§ããã¹ã¿ããã¯çŽ50人ã§ããã®ãã¡20人ã¯éçºè
ã§ãã çŸåšã4ã€ã®éçºããŒã ããããããããã«5人ã®ãã«ã¹ã¿ãã¯éçºè
ãããŸãã ããããèªåèªèº«ããã«ã¹ã¿ãã¯ã®éçºè
ãšåŒã¶ããšãšãSQL Serverã®äœæ¥ãASP.NETãCïŒã§ã®éçºãOOPãDDDãHTMLãCSSãJSãçç¥ãããã¹ãŠãè³¢ã䜿çšã§ããããšãç解
ããããšã¯æ¬åœã«
è¯ãããšã§ãã ãã¡ãããåéçºè
ã¯ç°ãªããã®ã«åŒãå¯ããããŸãããç§ãã¡å
šå¡ãäœããã®æ¹æ³ã§.NETéçºã®å°é家ã§ãããã³ãŒãã®90ïŒ
ã¯CïŒã§èšè¿°ãããŠããŸãã
åœç€Ÿã®è£œå-ããŒã±ãã£ã³ã°èªååã·ã¹ãã -ã¯ãç¹å®ã®ã¯ã©ã€ã¢ã³ãããšã«å€§éã®èšå®ãå¿
èŠãšããŸãã ãããŒãžã£ãŒã顧客åãã«è£œåãã«ã¹ã¿ãã€ãºã§ããããã«ããããã«ãéµéãéå§ããããããªã¬ãŒããã®ä»ã®ã¡ã«ããºã ãäœæãããããµãŒãã¹ãã«ã¹ã¿ãã€ãºãããããããšãã§ãã管çãµã€ãããããŸãã ãã®ç®¡çãµã€ãã«ã¯å€ãã®ããŸããŸãªéèªæãªUIãå«ãŸããŠãããã«ã¹ã¿ãã€ãºãããã€ã³ãã现ãããªãã°ãªãã»ã©ãéçšç°å¢ã§ãªãªãŒã¹ããæ©èœãå¢ããã»ã©ãUIã¯èå³æ·±ããã®ã«ãªããŸãã
補åã«ããŽãªã§çµã蟌ã 以åããã®ãããªUIã®éçºã«ã©ã®ããã«å¯ŸåŠããŸãããïŒ ããŸã察åŠã§ããŸããã§ããã åºæ¬çã«ãAjaxãåä¿¡ããHTMLã®äžéšããµãŒããŒäžã§ã¬ã³ããªã³ã°ããããšã«æåããŸããã ãŸãã¯ãjQueryã䜿çšããã€ãã³ãã§ã®ã¿ã ãŠãŒã¶ãŒã«ãšã£ãŠãããã¯éåžžãç¶ç¶çãªããŠã³ããŒãããããã¿ããšã®ããªããŒããŒãå¥åŠãªãã°ããããããŸããã éçºè
ã®èŠ³ç¹ããèŠããšããããã¯èª°ããæããŠããæ¬åœã®ãã¹ã¿ã§ããã èšç»ã®UIã®ãã±ããã¯ãã¹ãŠãLã®èŠç©ãããããã«åãåããã³ãŒããèšè¿°ãããšãã«å€§éã®ãã¿ã³ããŒã«ã«æ³šãããŸããã ãããŠããã¡ããããã®ãããªUIã«é¢é£ããå€ãã®ãã°ããããŸããã ããã¯æ¬¡ã®ããã«èµ·ãããŸãããæåã®å®è£
ã§ã¯ãããã€ãã®å°ããªééããè¡ãããŸããã ãããŠããã®å¥è·¡ã®ãã¹ãããªãã£ããããä»ã®äœãã修埩ãããšããå¿
ç¶çã«ãã©ãã©ã«ãªããŸããã
人çããã®äŸã ãããæäœäœæããŒãžã§ãã ããžãã¹ã«ã€ããŠè©³ãã説æããªããŠããç§ãã¡ãšã®æäœã¯ãã¯ã©ã€ã¢ã³ãã®è«è² æ¥è
ã䜿çšã§ããRESTãµãŒãã¹ã®ãããªãã®ã ãšããèšããŸããã ãã®æäœã§ã¯ãæ¶è²»è
ç»é²ã®æ®µéã«å¿ããŠå¯çšæ§ã«å¶éããããæ§æããããã«æ¬¡ã®ãããªå¶åŸ¡ããããŸããã
ãããŠããã®ã³ã³ãããŒã«ã®å€ãã³ãŒãã¯æ¬¡ã®ãšããã§ãã
æäœå¯çšæ§è¡šç€ºå¶åŸ¡ã³ãŒããã¥ãŒã®ã¹ã©ã€ã¹
<h2 class="column-header"> <span class="link-action" data-event-name="ToggleElements" data-event-param='{"selector":"#WorkFlowAllowance", "callback": "toggleWorkflowAvailability"}'> </span> </h2> @Html.HiddenFor(m => m.IsAllowedForAllWorkflow, new { Id = "IsAllowedForAllWorkflow" }) <div id="WorkFlowAllowance" class="@(Model.IsAllowedForAllWorkflow ? "none" : string.Empty) row form_horizontal"> <table class="table table_hover table_control @(Model.OperationWorkflowAllowances.Any() ? String.Empty : "none")" id="operationAllowanceTable"> <thead> <tr> <th> </th> <th></th> </tr> </thead> <tbody> @Model.OperationWorkflowAllowances.Each( @<tr> <td> @item.Item.WorkflowDisplayName <input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[@(item.Index)].WorkflowName" value="@item.Item.WorkflowName" /> <input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[@(item.Index)].WorkflowDisplayName" value="@item.Item.WorkflowDisplayName" /> <input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[@(item.Index)].Id" value="@item.Item.Id" /> </td> <td> <button class="cell-grid__right button button_icon-only button_red removeOperationAllowance"><span class="icon icon_del"></span></button> <span class="cell-grid__wraps">@(item.Item.StageName ?? "")</span> <input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[@(item.Index)].StageName" value="@item.Item.StageName" /> <input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[@(item.Index)].StageDisplayName" value="@item.Item.StageDisplayName" /> </td> </tr>) </tbody> </table> <div class="col col_462"> <div class="form-group form-group_all"> </div> @if (Model.WorkFlows.Any()) { <div> <div class="form-group"> <label class="form-label"><span> </span></label> @Html.DropDownList("WorkflowList", Model.WorkFlows, new Dictionary<string, object> { { "class", "form-control select2 w470" }, { "data-placeholder", " " }, { "id", "workflowList" }, { "disabled", "disabled" } }) </div> <div class="form-group"> <div class="form-list"> <input id="isAllowedForAllStagesForCurrentWorkflow" type="checkbox" name="StageMechanicsRegistratioName" autocomplete="off"> <label for="isAllowedForAllStagesForCurrentWorkflow"> <span id="exceptAnonymus"></span><span id="workflowName"></span></label> </div> </div> <div class="form-group"> <label class="form-label"><span></span></label> @Html.DropDownList("WorkflowStageList", new SelectListItem[0], new Dictionary<string, object> { { "class", "form-control select2 w470" }, { "data-placeholder", " " }, { "id", "workflowStageList" }, { "disabled", "disabled"} }) </div> <div class="form-group"> <button class="button button_blue" id="addOperationAllowance"> </button> </div> </div> } else { @: } </div> </div>
ãããŠããã®ãã¥ãŒãæ©èœãããjsã¯æ¬¡ã®ãšããã§ãïŒå®è¡å¯èœãªã³ãŒãã衚瀺ããããšãç®çãšããŠããã®ã§ã¯ãªãããããã©ãã»ã©æ²ããã£ããã瀺ããŠããŸãïŒã
function initOperationAllowance(typeSelector) { $('#workflowList').prop('disabled', false); $('#workflowList').trigger('change'); if ($(typeSelector).val() == 'PerformAction') { $('#exceptAnonymus').html('( )'); } else { $('#exceptAnonymus').html(''); } } function toggleWorkflowAvailability() { var element = $("#IsAllowedForAllWorkflow"); $('#operationAllowanceTable tbody tr').remove(); parameters.selectedAllowances = []; return element.val().toLowerCase() == 'true' ? element.val(false) : element.val(true); } function deleteRow(row) { var index = getRowIndex(row); row.remove(); parameters.selectedAllowances.splice(index, 1); $('#operationAllowanceTable input').each(function () { var currentIndex = getFieldIndex($(this)); if (currentIndex > index) { decrementIndex($(this), currentIndex); } }); if (parameters.selectedAllowances.length == 0) { $('#operationAllowanceTable').hide(); } } function updateWorkflowSteps(operationType) { var workflow = $('#workflowList').val(); if (workflow == '') { $('#isAllowedForAllStagesForCurrentWorkflow') .prop('checked', false) .prop('disabled', 'disabled'); refreshOptionList( $('#workflowStageList'), [{ Text: ' ', Value: '', Selected: true }] ); $('#workflowStageList').trigger('change').select2('enable', false); return; } var url = parameters.stagesUrlTemplate + '?workflowName=' + workflow + '&OperationTypeName=' + operationType; $.getJSON(url, null, function (data) { $('#isAllowedForAllStagesForCurrentWorkflow') .prop('checked', false) .removeProp('disabled'); refreshOptionList($('#workflowStageList'), data); $('#workflowStageList').trigger('change').select2('enable', true); }); } function refreshOptionList(list, data) { list.find('option').remove(); $.each(data, function (index, itemData) { var option = new Option(itemData.Text, itemData.Value, null, itemData.Selected); list[0].add(option); }); } function AddRow(data) { var rowsCount = $('#operationAllowanceTable tr').length; var index = rowsCount - 1; var result = '<tr ' + (rowsCount % 2 != 0 ? 'class="bgGray">' : '>') + '<td>' + '{DisplayWorkflowName}' + '<input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[' + index + '].WorkflowName" value="{WorkflowName}"/>' + '<input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[' + index + '].Id" value=""/>' + '<input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[' + index + '].WorkflowDisplayName" value="{DisplayWorkflowName}"/>' + '</td>' + '<td>' + '<button class="cell-grid__right button button_icon-small button_red removeOperationAllowance"><span class="icon icon_del"></span></button>' + '<span class="cell-grid__wraps">{DisplayStageName}</span>' + '<input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[' + index + '].StageName" value="{StageName}"/>' + '<input type="hidden" name="OperationTypeViewModel.OperationWorkflowAllowances[' + index + '].StageDisplayName" value="{DisplayStageName}"/>' + '</td>' + '</tr>'; for (key in data) { result = result.replace(new RegExp('{' + key + '}', 'g'), data[key]); } $('#operationAllowanceTable').show().append(result); } function IsValidForm() { var result = ValidateList($('#workflowList'), ' ') & ValidateListWithCheckBox($('#workflowStageList'), $('#isAllowedForAllStagesForCurrentWorkflow'), ' '); if (!result) return false; var workflowName = $('#workflowList').val(); var stageName = ''; if (!$('#isAllowedForAllStagesForCurrentWorkflow').is(':checked')) { stageName = $('#workflowStageList').val(); } hideError($('#workflowList')); hideError($('#workflowStageList')); for (var i = 0; i < parameters.selectedAllowances.length; i++) { if (parameters.selectedAllowances[i].workflow == workflowName && parameters.selectedAllowances[i].stage == stageName) { if (stageName == '') { showError($('#workflowList'), ' '); } else { showError($('#workflowStageList'), ' '); } result = false; } else if (parameters.selectedAllowances[i].workflow == workflowName && parameters.selectedAllowances[i].stage == '') { showError($('#workflowList'), ' '); result = false; } } return result; } function ValidateList(field, message) { if (field.val() == "") { showError(field, message); return false; } hideError(field); return true; } function ValidateListWithCheckBox(field, checkBoxField, message) { if (!checkBoxField.prop('checked')) { return ValidateList(field, message); } hideError(field); return true; } function showError(field, message) { if (typeof (message) === 'undefined') { message = ' '; } field.addClass('input-validation-error form-control_error'); field.parent('.form-group').find('div.tooltip-error').remove(); field.closest('.form-group').append( '<div class="tooltip-icon tooltip-icon_error"><div class="tooltip-icon__content">' + '<strong></strong><br>' + message + '</div></div>'); } function hideError(field) { field.removeClass('input-validation-error form-control_error'); field.parent('.form-group').find('div.tooltip-icon_error').remove(); } function getRowIndex(row) { return getFieldIndex(row.find('input:first')); } function getFieldIndex(field) { var name = field.prop('name'); var startIndex = name.indexOf('[') + 1; var endIndex = name.indexOf(']'); return name.substr(startIndex, endIndex - startIndex); } function decrementIndex(field, index) { var name = field.prop('name'); var newIndex = index - 1; field.prop('name', name.replace('[' + index + ']', '[' + newIndex + ']')); } function InitializeWorkflowAllowance(settings) { $(function() { parameters.selectedAllowances = settings.selectedAllowances; initOperationAllowance(parameters.typeSelector); $('#workflowList').change(function () { updateWorkflowSteps($(parameters.typeSelector).val()); }); $('#addOperationAllowance').click(function (event) { event.preventDefault(); if (IsValidForm()) { var data = { 'StageName': $('#workflowStageList').val(), 'WorkflowName': $('#workflowList').val(), }; if ($('#isAllowedForAllStagesForCurrentWorkflow').is(':checked')) { data.DisplayWorkflowName = $('#workflowList option[value=' + data.WorkflowName + ']').text(); data.DisplayStageName = ''; data.StageName = ''; } else { data.DisplayWorkflowName = $('#workflowList option[value=' + data.WorkflowName + ']').text(); data.DisplayStageName = $('#workflowStageList option[value=' + data.StageName + ']').text(); } AddRow(data); if (data.StageName == '') { var indexes = [];
æ°ããåžæ
ããæç¹ã§ããã®ããã«çããããšã¯ãã¯ãäžå¯èœã§ããããšã«æ°ã¥ããŸããã ããã€ãã®è°è«ã®åŸãããã³ããšã³ããç解ããçã®éãæ瀺ããåŽã®äººãå¿
èŠã§ãããšããçµè«ã«éããŸããã Reactã®äœ¿çšãææ¡ããããªãŒã©ã³ãµãŒãéããŸããã 圌ã¯ç§ãã¡ãšããŸãä»äºãããŠããŸããã§ããããäœãèµ·ãã£ãŠããã®ãã瀺ãããã«ããã€ãã®ã³ã³ãããŒã«ãäœæããããšãã§ããŸããã
å
¬åŒãŠã§ããµã€ãã®ãã¥ãŒããªã¢ã«ãå®äºããŠããReactãæ¬åœã«å¥œãã§ãããã誰ãããããæ°ã«å
¥ããŸããã§ããã ããã«ãçéå
¥ãã®ããã³ããšã³ãéçºè
ã¯javascriptãæããŠããŸãããéçãªåã®éçºã®äžçã§ã¯ãjavascriptã¯ïŒè»œåºŠã«èšãã°ïŒäººæ°ããªãããã䜿çšããããã«æäŸããããããã®Webããã¯ãããªã声ã¯ãã¹ãŠæãã£ãŠããŸãã ãã®çµæã察åŠããå¿
èŠããããã¬ãŒã ã¯ãŒã¯ã決å®ããããã«ãç°ãªããã¬ãŒã ã¯ãŒã¯ã䜿çšããŠãè€éãªUIã®ããã€ãã®ãããã¿ã€ããäœæããããšã決å®ãããŸããã éžæããåãã¬ãŒã ã¯ãŒã¯ã®ãµããŒã¿ãŒã¯ãã³ãŒããæ¯èŒã§ããããã«åãã³ã³ãããŒã«ã®ãããã¿ã€ããäœæããå¿
èŠããããŸããã AngularãReactãKnockoutãæ¯èŒããŸããã åŸè
ã¯ãããã¿ã€ã段éãçµãããšãããããŸããã§ããããç§ã¯ã©ããªçç±ã§èŠããŠããããšãããããŸããã ããããAngularãšReactã®æ¯æè
ã®éã§ãäŒç€Ÿã¯çã®å
æŠãéå§ããŸããïŒ
åè«:)å®éãåãã¬ãŒã ã¯ãŒã¯ã«ã¯1人ã®ãµããŒã¿ãŒãããŸããããä»ã®èª°ããã©ã¡ãã奜ãã§ã¯ãããŸããã§ããã 誰ãããããããäœã決ããããªãã£ãã Angularã§ã¯ã誰ãããã®è€éãã«æ©ãŸãããReactã§ã¯ããã®åœæã®Visual Studioã§ã®ãµããŒãã®æ¬ åŠãæ¬åœã«éåžžã«äžæå¿«ãªäºå®ã§ãã£ããæããªæ§æã§ããã
幞ããªããšã«ãç§ãã¡ã®äžåžïŒäŒç€Ÿã®ææè
ã®1人ïŒãç§ãã¡ãå©ããŠãããŸããããã¡ãããé·ãéããã°ã©ã ããŠããŸããã§ãããã圌ã®æã¯èåãä¿ã£ãŠããŸãã ãããã¿ã€ããå¹æããããããªãããšãæããã«ãªããéçºãäœããã®çç±ã§æéã浪費ããããã«ãªã£ãåŸïŒãã®æç¹ã§ãæ¯èŒã®ããã®ã³ãŒããå¢ããããã«ãããã«å€§ããªãµã€ãºã®å¥ã®ãããã¿ã€ããäœæããèšç»ãç«ãŠãŠããŸããïŒïŒã圌ã ããŠããªã圌ã®éžæããŸã Reactã«èœã¡ãããæãåºããŠãSasha
agornik Gornikã¯ç§ã«æ¬¡ã®ããã«èšã£ãïŒç§ã¯ããªããŒã®ããã§ã¯ãªã圌ã®èšèãåŒçšãããããã¯åãªãæèŠã§ããã ïŒ
ããã€ãã®ãããã¿ã€ãããããŸããïŒåå¿ãè§åºŠãããã³ä»ã®ãã®ã èŠãŸããã ç§ã¯è§ã奜ãã§ã¯ãªããåå¿ã奜ãã§ããã
ãããã[倧声ã§]倧声ã§å«ã³ãä»ã®ã¿ããªã¯éèã®ããã§ããã èªãã§èŠãªããã°ãªããŸããã§ããã
ãã®åå¿ã¯ãå€ãã®ã¯ãŒã«ãªãµã€ãã§çç£ãããŠããããšãããããŸããã FBãYahooãWhatsAppãªã©ããããŸãã æããã«å·šå€§ãªæ¡çšãæ¥ãŠãããæªæ¥ããããŸãã
ãããŠæ ŒçŽåº«ã§-[äœãè¯ãããšã¯ãããŸãã]ã æªæ¥ãèŠãŸããã 2.0ã§åŒ·åãããã¢ã³ã°ã«ã®ãããã¿ã€ãã§ãç§ã奜ãŸãªãã£ããã®ã¯ãã¹ãŠèŠãŸããã
åå¿ã¯äººçã®ããã«äœããããã®ã§ãããç¹å®ã®åé¡ã解決ãããã®ã§ããããšã«æ°ã¥ãã ãããŠè§åºŠ-Googleã®è³ããã®çè«å®¶ã¯ããã²ããçãããããããçš®é¡ã®æŠå¿µãæãã€ããŸãã GWTãŸãã¯ãããäœã§ãããããã§ããã
ãŸããç§ã¯åŒ·ãæå¿ã§éèã®å³æ¹ãããå¿
èŠãããããšã«æ°ã¥ããŸããããããªããã°æŽŸæã§ééã£ããã®ãåã€ã§ãããã ãããè¡ãåã«ã3,300äžã®èšŒæ ãšãªã³ã¯ããã£ã³ãã«ã«æã蟌ã¿ã[ããŒãã¢ãŒããã¯ã]ã®æ¯æŽãæ±ãã誰ã倢äžã«ãªããªãããã«åªããŸããã
ãŸããå°çã®ãããªéèŠãªè°è«ãæãåºããŸããã åå¿ã®ããã«ãããã段éçã«å®è¡ããŠæ¢åã®ããŒãžã«ãã蟌ãçŸããæ¹æ³ãšãããããå®å
šã«ããçŽãã®ã«å¿
èŠãªè§åºŠããããããã[貧ãã]ã¢ãŒããã¯ãã£ã§ä¿®æ£ãããŸãã
ããããç§ã¯ãŸããåå¿ãšããŠãçè«çã«ã¯ãUIãWebã«å¯ŸããŠãå®è¡ã§ããããšãèªã¿ãŸããã ãããŠããã¹ãŠã®ãµãŒããŒåŽã®js /ããã«åå¿ããããããã¹ãŠè¡ãå Žæã ãããŠæåŸã«ãããªãã¯åäžã®è°è«ãåãããšãã§ããŸããã§ããã
ã¹ã¿ãžãªã®ãµããŒãã¯ããã«åæžãããããšã«æ°ä»ããŸããã æçµçã«ããã¹ãŠããŸã£ããåãããã«èµ·ãããŸããã ç§ã¯ç¢ºãã«ãã®æ±ºå®ã«ãšãŠãããããã§ãïŒ
ã©ãããã®ïŒ
ã«ãŒããå
¬éããUIãã©ã®ããã«èª¿çããŠãããã瀺ããŸãã ãã¡ãããããã³ããšã³ãã®ã¢ãŒãã£ã¹ãã¯ä»ããç¬ãå§ããŸãããç§ãã¡ã«ãšã£ãŠãã®ã³ãŒãã¯æ¬åœã®åå©ã§ããããšãŠãæºè¶³ããŠããŸã:)
ããšãã°ãè¿œå ã®ãã£ãŒã«ããäœæããããã«ããŒãžã䜿çšããŸãã ç°¡åãªããžãã¹åç
§ïŒæ¶è²»è
ã泚æã賌å
¥ã補åãªã©ã®äžéšã®ãšã³ãã£ãã£ã«ã¯ã顧客åºæã®ããŒã¿ãé¢é£ä»ããããŠããå ŽåããããŸãã ãã®ãããªããŒã¿ãä¿åããããã«ãåŸæ¥ã®
ãšã³ãã£ãã£-å±æ§-å€ã¢ãã«ã䜿çšã
ãŸã ã æåã¯ãåã¯ã©ã€ã¢ã³ãã®è¿œå ãã£ãŒã«ããïŒéçºæéãç¯çŽããããã«ïŒããŒã¿ããŒã¹ã«çŽæ¥å
¥åãããŸããããæåŸã«ãUIã®æéãèŠã€ãããŸããã
ãããžã§ã¯ãã«ãã£ãŒã«ããè¿œå ããããŒãžã¯æ¬¡ã®ãšããã§ãã
åæåã®ãã£ãŒã«ããè¿œå ãã Stringåã®ãã£ãŒã«ããè¿œå ãã ãããŠãReactäžã§ãã®ããŒãžã®ã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã
è¿œå ã®ãã£ãŒã«ããè¿œå /ç·šéããããã®ããŒãžã®ã³ã³ããŒãã³ã TypeScript
ãããã¯äœã§ãããïŒãjavascriptã衚瀺ãããããšãæåŸ
ããŠãããã©ãããå°ããããšãã§ããŸãã ããã¯tsxã§ã-TypeScriptã§ã®Reactã®jsxã®ããªã¢ã³ãã§ãã UIã¯å®å
šã«éçã«åä»ããããŠããããããžãã¯ã©ã€ã³ãã¯ãããŸããã åæããŸããããã¯ç§ãã¡ã®ãããªçéå
¥ãã®ããã¯ãšã³ãããæåŸ
ã§ããŸã:)
ããã€ãã®èšèããããŸãã ç§ã¯éçããã³åçã«åä»ããããèšèªã®ãããã¯ã§ããªããŒãäžãããšããç®æšã¯ãããŸããã ç§ãã¡ã®äŒç€Ÿã§ã¯ãåçèšèªã奜ã人ã¯ããŸããã§ããã ç§ãã¡ã¯ãé·å¹Žã«ããã£ãŠãªãã¡ã¯ã¿ãªã³ã°ãããŠãã倧èŠæš¡ãªãµããŒããããžã§ã¯ããäœæããããšã¯ããã»ã©é£ãã
ãªããšèããŠããŸãã IntelliSenseãæ©èœããªããããæžãã®ã¯é£ããã§ã:)ãããç§ãã¡ã®ä¿¡å¿µã§ãã ãã¹ãŠããã¹ãã§ã«ããŒã§ãããšäž»åŒµããããšãã§ããããããåçã«åä»ããããèšèªã§å¯èœã«ãªããŸããããã®ãããã¯ã«ã€ããŠã¯è°è«ããŸããã
tsx圢åŒã¯ãã¹ã¿ãžãªãšå¥ã®éåžžã«éèŠãªãã€ã³ãã§ããæ°ããRïŒã§ãµããŒããããŠããŸãã ãããã1幎åïŒRïŒãšã¯ç°ãªãïŒã¹ã¿ãžãªã§ã¯jsxã®ãµããŒããããããŸããã§ãããjsã®éçºã«ã¯å¥ã®ã³ãŒããšãã£ã¿ãŒãå¿
èŠã§ããïŒSublimeãšAtomã䜿çšããŸããïŒã ãã®çµæãã¹ã¿ãžãªãœãªã¥ãŒã·ã§ã³ã§ã¯ãã¡ã€ã«ã®ååãååã§ã¯ãªããèå±ãè¿œå ãããã ãã§ããã 幞çŠã¯ãã§ã«æ¥ãŠããã®ã§ãããã«ã€ããŠã¯è©±ããŸãããã
çŽç²ãªåœ¢åŒã®typescriptã§ãããç§ãã¡ãæãéçåä»ãã®ã¬ãã«ãäžããªãããšã«æ³šæãã¹ãã§ãã ããšãã°ãã¢ãã«ã«ããã€ãã®ããããã£ãèšå®ããå ŽåïŒå®éã«UIã³ã³ãããŒã©ãŒãããã€ãã®ã¢ãã«ããããã£ã«ãã€ã³ãããããïŒããã®ãããªããããã£ããšã«é·ãéã³ãŒã«ããã¯é¢æ°ãèšè¿°ããããããã£ã®ååãåãã³ãŒã«ããã¯ã1ã€äœ¿çšã§ããŸããéçã«å
¥åãããããšã¯ãããŸããã å
·äœçã«ã¯ããã®åé¡ãããããã®ã³ãŒãã§è§£æ±ºããŸããïŒäžèšã®getPropertySetterã®äœ¿çšäŸãåç
§ã§ããŸãïŒã
getPropertyNameByPropertyProviderã®å®è£
ãéåžžã«éŠ¬é¹¿ããŠããããšã¯ééããããŸããïŒå¥ã®åèªãéžã¶ããšãããããŸããïŒã ããããtypescriptã¯ãŸã å¥ã®éžæè¢ãæäŸããŸããã ExpressionTreeãšnameofã¯å«ãŸããŠããŸãããgetPropertySetterã®ãã©ã¹ã®åŽé¢ã¯ããã®ãããªå®è£
ã®ãã€ãã¹ã®åŽé¢ãäžåããŸãã æåŸã«ã圌女ã«äœãèµ·ããå¯èœæ§ããããŸããïŒ ããæç¹ã§é床ãäœäžãå§ããå¯èœæ§ããããããã«ãã£ãã·ã¥ãå²ãåœãŠãããšãã§ããŸãããŸãã¯ããã®é ã«ã¯typescriptã®nameofãå®è¡ãããŸãã
ãã®ãããª
ããã¯ã®ãããã§ãããšãã°ãã³ãŒãå
šäœã§ååãå€æŽããŠããã®ã§ãã©ããã§äœãããã©ãã©ã«ãªãããšãå¿é
ããå¿
èŠã¯ãããŸããã
ããã§ãªããã°ããã¹ãŠãéæ³ã®ããã«æ©èœããŸãã ã³ã³ããŒãã³ãã«å¿
èŠãªå°éå
·ãæå®ããŸããã§ãããïŒ ã³ã³ãã€ã«ãšã©ãŒã ééã£ãã¿ã€ãã®ãããããã³ã³ããŒãã³ãã«æž¡ããŸãããïŒ ã³ã³ãã€ã«ãšã©ãŒã å®è¡æã®èŠåä»ãã®æããªPropTypeã¯ãããŸããã ããã§ã®å¯äžã®åé¡ã¯ãtypescriptã§ã¯ãªãCïŒã§ããã¯ãšã³ããä¿æããŠããããšã§ãããã®ãããã¯ã©ã€ã¢ã³ãã§äœ¿çšãããåã¢ãã«ã¯ããµãŒããŒãšã¯ã©ã€ã¢ã³ãã§2åèšè¿°ããå¿
èŠããããŸãã ãã ãããã®åé¡ã«ã¯è§£æ±ºçããããŸããç§ãã¡ã¯ã.NETã®åããã®typescriptã®ãããã¿ã€ãåãžã§ãã¬ãŒã¿ãŒãäœæããŸããã ãã®ãŠãŒãã£ãªãã£ãäœããã®æ¹æ³ã§é©çšããæŠéç¶æ
ã§ã®åäœã確èªããå¿
èŠãããããã§ãã ã©ãããããã¹ãŠããã§ã«å€§äžå€«ã§ãã
ã³ã³ããŒãã³ãã¬ã³ããªã³ã°
ããŒãžãéããšãã«ã³ã³ããŒãã³ããåæåããæ¹æ³ãšããµãŒããŒã³ãŒããšå¯Ÿè©±ããæ¹æ³ã«ã€ããŠè©³ãã説æããŸãã ã«ããªã³ã°ãéåžžã«é«ãããšãããã«èŠåããŸãããäœãã§ããŸããã
ãµãŒããŒäžã®åã³ã³ããŒãã³ãã«ã¯ãPOSTèŠæ±äžã«ãã®ã³ã³ããŒãã³ãããã€ã³ããããã¥ãŒã¢ãã«ããããŸãã éåžžãæåããã³ã³ããŒãã³ããåæåããããã«åããã¥ãŒã¢ãã«ã䜿çšãããŸãã ããšãã°ãäžèšã®è¿œå ãã£ãŒã«ãããŒãžã®ãã¥ãŒã¢ãã«ãåæåããã³ãŒãïŒCïŒïŒã¯æ¬¡ã®ãšããã§ãã
ãµãŒããŒäžã®ã¢ãã«åæåã³ãŒãã衚瀺ãã public void PrepareForViewing(MvcModelContext mvcModelContext) { ComponentsMap = ModelApplicationHostController .Instance .Get<ReactComponentViewModelConfiguration>() .GetNamedObjectRelatedComponentsMapFor<CustomFieldKindTypedViewModelBase, CustomFieldType>( customFieldViewModel => customFieldViewModel.PrepareForViewing(mvcModelContext)); EntityTypes = ModelApplicationHostController.NamedObjects .GetAll<CustomFieldKindEntityType>() .Select( type => new EntityTypeDto { Name = type.Name, SystemName = type.SystemName, Prefix = type.ColumnPrefix }) .ToArray(); if (ModelApplicationHostController.NamedObjects.Get<DirectCrmFeatureComponent>().Sku.IsEnabled()) { EntityTypes = EntityTypes.Where( et => et.SystemName != ModelApplicationHostController.NamedObjects .Get<CustomFieldKindEntityTypeComponent>().Purchase.SystemName) .ToArray(); } else { EntityTypes = EntityTypes.Where( et => et.SystemName != ModelApplicationHostController.NamedObjects .Get<CustomFieldKindEntityTypeComponent>().Sku.SystemName) .ToArray(); } if (FieldType.IsNullOrEmpty()) { TypedViewModel = new StringCustomFieldKindTypedViewModel(); FieldType = TypedViewModel.Type; } }
ããã§ã¯ãããã€ãã®ããããã£ãšã³ã¬ã¯ã·ã§ã³ãåæåããããªã¹ããäœæããããã«äœ¿çšãããŸãã
ãã®ãã¥ãŒã¢ãã«ã®ããŒã¿ã䜿çšããŠã³ã³ããŒãã³ããæç»ããããã«ãExtensionã¡ãœããHtmlHelperãèšè¿°ãããŠããŸãã å®éãã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããå¿
èŠãããå Žæã§ã¯ã次ã®ã³ãŒãã䜿çšããŸãã
@Html.ReactJsFor("DirectCrm.SaveCustomFieldKindComponent", m => m.Value)
æåã®ãã©ã¡ãŒã¿ãŒã¯ã³ã³ããŒãã³ãã®ååã2çªç®ã¯PropertyExpression-ãã®ã³ã³ããŒãã³ãã®ããŒã¿ãé
眮ãããŠããããŒãžã®ãã¥ãŒã¢ãã«ã®ãã¹ã§ãã ãã®ã¡ãœããã®ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
public static IHtmlString ReactJsFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, string componentName, Expression<Func<TModel, TProperty>> expression, object initializeObject = null) { var validationData = htmlHelper.JsonValidationMessagesFor(expression); var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); var modelData = JsonConvert.SerializeObject( metadata.Model, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, TypeNameAssemblyFormat = FormatterAssemblyStyle.Full, Converters = { new StringEnumConverter() } }); var initializeData = JsonConvert.SerializeObject(initializeObject); return new HtmlString(string.Format( "<div data-react-component='{0}' data-react-model-name='{1}' data-react-model='{2}' " + "data-react-validation-summary='{3}' data-react-initialize='{4}'></div>", HttpUtility.HtmlEncode(componentName), HttpUtility.HtmlEncode(htmlHelper.NameFor(expression)), HttpUtility.HtmlEncode(modelData), HttpUtility.HtmlEncode(validationData), HttpUtility.HtmlEncode(initializeData))); }
, div, , : , , , , , - . div :
function initializeReact(context) { $('div[data-react-component]', context).each(function () { var that = this; var data = $(that).data(); var component = eval(data.reactComponent); if (data.reactInitialize == null) { data.reactInitialize = {}; } var props = $.extend({ model: data.reactModel, validationSummary: data.reactValidationSummary, modelName: data.reactModelName }, data.reactInitialize); React.render( React.createElement(component, props), that ); }); }
, â state. , ( / select').
Binding
, , ?
. . . , , ( , ), hidden input, , json. , json ASP.NET , ModelBinder.
hidden input'. :
<HiddenInputJsonSerializer model={this._getViewModelValue() } name={this.props.modelName} />
:
class HiddenInputJsonSerializer extends React.Component<{ model: any, name: string }, {}> { render() { var json = JSON.stringify(this.props.model); var name = this.props.name; return ( <input type="hidden" value={json} name={name} /> ); } }
â json , this.props.modelName â , data-react-model-name (. ), - -, json'.
, json - , . , -, json', JsonBindedAttribute. -, -, json:
public class CustomFieldKindCreatePageViewModel : AdministrationSiteMasterViewModel { public CustomFieldKindCreatePageViewModel() { Value = new CustomFieldKindValueViewModel(); } [JsonBinded] public CustomFieldKindValueViewModel Value { get; set; }
, - CustomFieldKindCreatePageViewModel.Value . - â ModelBinder. : JsonBindedAttribute â , CustomFieldKindValueViewModel ( ). :
, json public class MindboxDefaultModelBinder : DefaultModelBinder { private object DeserializeJson( string json, Type type, string fieldNamePrefix, ModelBindingContext bindingContext, ControllerContext controllerContext) { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead, Converters = new JsonConverter[] { new ReactComponentPolimorphicViewModelConverter(), new FormBindedConverter(controllerContext, bindingContext, fieldNamePrefix) } }; return JsonConvert.DeserializeObject(json, type, settings); } protected override void BindProperty( ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) { if (!propertyDescriptor.Attributes.OfType<JsonBindedAttribute>().Any()) { base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } } public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var result = base.BindModel(controllerContext, bindingContext);
, , json, json , , 99.9% - , - . , .
, , html, , react-
. , - react', , react'. , , . , :
, «», js , react â , . js, , html , js . , , UI-, , react. « », , react' .
? , , input' name, , react. input' hidden input', - . , POST-, , -, , JsonBindedAttribute, , json. , - , FormBindedAttribute, json FormBindedConverter, :
FormBindedConverter public class FormBindedConverter : JsonConverter { private readonly ControllerContext controllerContext; private readonly ModelBindingContext parentBindingContext; private readonly string formNamePrefix; private Type currentType = null; private static readonly Type[] primitiveTypes = new[] { typeof(int), typeof(bool), typeof(long), typeof(decimal), typeof(string) }; public FormBindedConverter( ControllerContext controllerContext, ModelBindingContext parentBindingContext, string formNamePrefix) { this.controllerContext = controllerContext; this.parentBindingContext = parentBindingContext; this.formNamePrefix = formNamePrefix; } public override bool CanConvert(Type objectType) { return currentType != objectType && !primitiveTypes.Contains(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var currentJsonPath = reader.Path; currentType = objectType; var result = serializer.Deserialize(reader, objectType); currentType = null; if (result == null) return null; var resultType = result.GetType(); var formBindedProperties = resultType.GetProperties().Where(p => p.HasCustomAttribute<FormBindedAttribute>()); foreach (var formBindedProperty in formBindedProperties) { var formBindedPropertyName = formBindedProperty.Name; var formBindedPropertyFullPath = $"{formNamePrefix}.{currentJsonPath}.{formBindedPropertyName}"; var formBindedPropertyModelBinderAttribute = formBindedProperty.PropertyType.TryGetSingleAttribute<ModelBinderAttribute>(); var effectiveBinder = GetBinder(formBindedPropertyModelBinderAttribute); var formBindedObject = effectiveBinder.BindModel( controllerContext, new ModelBindingContext(parentBindingContext) { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType( () => formBindedProperty.GetValue(result), formBindedProperty.PropertyType), ModelName = formBindedPropertyFullPath }); formBindedProperty.SetValue(result, formBindedObject); } return result; } private static IModelBinder GetBinder(ModelBinderAttribute formBindedPropertyModelBinderAttribute) { IModelBinder effectiveBinder; if (formBindedPropertyModelBinderAttribute == null) { effectiveBinder = new MindboxDefaultModelBinder(); } else { effectiveBinder = formBindedPropertyModelBinderAttribute.GetBinder(); } return effectiveBinder; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
- json, FormBindedAttribute. - , , binder , binder .
MindboxDefaultModelBinder, FormBindedConverter, FilterViewModelBinder, MindboxDefaultModelBinder.
-
UI , . :
UI. , switch , . , , . :
module DirectCrm { export class StringCustomFieldKindComponent extends CustomFieldKindComponentBase { render() { var stringViewModel = this.props.value as StringCustomerFieldKindTypedBackendViewModel; var stringConstantData = this.props.constantComponentData as StringCustomFieldKindConstantComponentData; var validationContext = this.props.validationContext as IValidationContext<StringCustomerFieldKindTypedBackendViewModel>; return ( <div> {super.render() } <FormGroup label=" " validationMessage={validationContext.getValidationMessageFor(m => m.validationStrategySystemName) } > <div className="form-control"> <Commons.Select value={stringViewModel.validationStrategySystemName} width="normal" onChange={getPropertySetter( stringViewModel, vm => this.props.onChange(vm), m => m.validationStrategySystemName) } options={stringConstantData.validationStrategies} disabled={this.props.disabled}/> </div> </FormGroup> </div>); } } }
module DirectCrm { export class DefaultCustomFieldKindComponent extends CustomFieldKindComponentBase { } }
module DirectCrm { export class CustomFieldKindComponentBase extends React.Component<DirectCrm.CustomFieldKindTypedComponentProps, {}> { render() { return <FormGroup label = " " validationMessage = { this.props.validationMessageForFieldType } > <div className="form-control"> <Commons.Select value={this.props.fieldType} options={this.props.fieldTypeSelectOptions} width="normal" placeholder=" " onChange={this.props.handleFieldTypeChange} disabled = {this.props.disabled}/> </div> {this.renderTooltip() } </FormGroup> } renderTooltip() { return <Commons.Tooltip additionalClasses="tooltip-icon_help" message={this.props.constantComponentData.tooltipMessage }/> } } }
?
, :
_renderComponent = () => { var fieldTypeSelectOptions = Object.keys(this._componentsMap). map(key => { return { Text: this._componentsMap[key].name, Value: key }; }); var componentInfo = this._componentsMap[this.state.model.fieldType]; var TypedComponent = componentInfo.component; return ( <div> <div className="row form_horizontal"> <div className="col-group"> // <TypedComponent validationContext={this.state.validationContext.getValidationContextFor(m => m.typedViewModel) } onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.typedViewModel) } value={this.state.model.typedViewModel} fieldType={this.state.model.fieldType} validationMessageForFieldType={this.state.validationContext.getValidationMessageFor(m=> m.fieldType) } fieldTypeSelectOptions={fieldTypeSelectOptions} handleFieldTypeChange={this._handleFieldTypeChange} constantComponentData={componentInfo.constantComponentData} disabled={!this.state.model.isNew}/> </div> // </div>); }
, TypedComponent, _componentsMap. _componentsMap â , ( « ») componentInfo, , : , (, url- - ), .NET , , -. _componentsMap json :
ComponentsMap' "componentsMap":{ "Integer":{ "name":"", "viewModelType":"Itc.DirectCrm.Web.IntegerCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "componentName":"DirectCrm.DefaultCustomFieldKindComponent", "constantComponentData":{ "$type":"Itc.DirectCrm.Web.IntegerCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "tooltipMessage":": 123456", "type":"Integer" } }, "String":{ "name":"", "viewModelType":"Itc.DirectCrm.Web.StringCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "componentName":"DirectCrm.StringCustomFieldKindComponent", "constantComponentData":{ "$type":"Itc.DirectCrm.Web.StringCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "validationStrategies":[ { "Disabled":false, "Group":null, "Selected":true, "Text":" ", "Value":"Default" }, { "Disabled":false, "Group":null, "Selected":false, "Text":" ", "Value":"IsValidLatinStringWithWhitespaces" }, { "Disabled":false, "Group":null, "Selected":false, "Text":" ", "Value":"IsValidLatinStringWithDigits" }, { "Disabled":false, "Group":null, "Selected":false, "Text":"", "Value":"IsValidDigitString" } ], "validationStrategySystemName":"Default", "tooltipMessage":": \"\"", "type":"String" } }, "Enum":{ "name":"", "viewModelType":"Itc.DirectCrm.Web.EnumCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "componentName":"DirectCrm.EnumCustomFieldKindComponent", "constantComponentData":{ "$type":"Itc.DirectCrm.Web.EnumCustomFieldKindTypedViewModel, itc.DirectCrm.Web, Version=6.459.0.3741, Culture=neutral, PublicKeyToken=null", "selectedEnumValues":null, "forceCreateEnumValue":false, "tooltipMessage":": - \"ExternalId\", - \"123\"", "type":"Enum" } } }
? . , ComponentsMap - :
public void PrepareForViewing(MvcModelContext mvcModelContext) { ComponentsMap = ModelApplicationHostController .Instance .Get<ReactComponentViewModelConfiguration>() .GetNamedObjectRelatedComponentsMapFor<CustomFieldKindTypedViewModelBase, CustomFieldType>( customFieldViewModel => customFieldViewModel.PrepareForViewing(mvcModelContext));
, ReactComponentViewModelConfiguration , - CustomFieldKindTypedViewModelBase, . :
configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new StringCustomFieldKindTypedViewModel()); configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new IntegerCustomFieldKindTypedViewModel()); configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new EnumCustomFieldKindTypedViewModel());
- , . - C# . , .
æ€èšŒ
:
, , - , . -
. , . , , , . , , .
. data-react-validation-summary (. ReactJsFor ). Validation summary â json, - ( ), , -. , validationSummary :
, , .
validation summary :
{ "typedViewModel":{ "selectedEnumValues[0]":{ "systemName":[ " 250 " ] } }, "name":[ " " ] }
, â , , . ValidationContext, validation summary, :
interface IValidationContext<TViewModel> { isValid: boolean; getValidationMessageFor: { (propertyExpression: {(model: TViewModel):any}): JSX.Element }; validationMessageExpandedFor: { (propertyExpression: {(model: TViewModel):any}): JSX.Element }; getValidationContextFor: { <TProperty>(propertyExpression: {(model: TViewModel):TProperty}): IValidationContext<TProperty> }; getValidationContextForCollection: { <TProperty>(propertyExpression: {(model: TViewModel):TProperty[]}): {(index: number): IValidationContext<TProperty>} } }
, . . , «»:
<FormGroup label="" validationMessage={this.state.validationContext.getValidationMessageFor(m => m.name) }> <Commons.Textbox value={this.state.model.name} width="normal" onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.name) } /> </FormGroup>
this.state.validationContext IValidationContext<CustomFieldKindValueBackendViewModel>, . getPropertyNameByPropertyProvider, , getValidationMessageFor validation summary .
, validation summary .
, - , . , , . , â -. - - . , , . , â , , , , , validation summary.
- «» :
private void RegisterEndUserInput( ISubmodelInputRegistrator<CustomFieldKindValueViewModel> registrator, CustomFieldKind customFieldKind) {
this â -, Name, , Name CustomFieldKind customFieldKind. , Name -.
CustomFieldKind :
public void Validate(ValidationContext validationContext) {
, , , CustomFieldKind.Name , , .
çµè«ãšããŠ
, UI. , , , :)
, , - , UI , Enterprise. , . ReactJS, - . -, , , , ! .