<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>
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 = []; // for (var i = 0; i < parameters.selectedAllowances.length; i++) { if (parameters.selectedAllowances[i].workflow == data.WorkflowName) { indexes.push(i); } } $("#operationAllowanceTable tbody tr").filter(function (index) { return $.inArray(index, indexes) > -1; }).each(function () { deleteRow($(this)); }); } parameters.selectedAllowances.push({ workflow: data.WorkflowName, stage: data.StageName }); $("#workflowList").val('').trigger('change'); updateWorkflowSteps($(parameters.typeSelector).val()); } }); $('#isAllowedForAllStagesForCurrentWorkflow').click(function () { if ($(this).is(":checked")) { $('#workflowStageList').prop('disabled', 'disabled'); } else { $('#workflowStageList').removeProp('disabled'); } }); $('#operationAllowanceTable').on('click', 'button.removeOperationAllowance', function (event) { var row = $(this).parent().parent(); setTimeout(function () { deleteRow(row); }, 20); event.preventDefault(); }); });
There were several prototypes: reactor, angular, and something else. I watched. I did not like Angular, I liked the reactor.
But [some] shouted the loudest, and everyone else was like vegetables. I had to read and watch.
I saw that the reactor was in production on a bunch of cool sites. FB, Yahoo, WhatsApp and something else there. Obviously, a huge admission is coming and there is a future.
And on angular - [nothing good]. I looked at the future. I saw that everything that I didn’t like in the prototype of the angulyar was strengthened in 2.0.
I understood that react is a thing for life made solving a specific problem. And the angular is the bearded theorists from Google from the brain come up with all sorts of concepts. As was the case with GWT or how it is there.
Well, I realized that we need a strong-willed decision to take the side of vegetables, otherwise the screaming, but wrong will win. Before I did this, I threw 33 million proofs and links into the channel, I enlisted the support of [our chief architect] and tried to make sure that nobody would stop.
And I remembered how hellishly important the argument was. For the reactor, there was a beautiful way to do it step by step and screw it into existing pages, and the angular required to redo them entirely, and this also corrected with [its bad] architecture.
Then I also read that in theory, in theory, you can even use the UI for the web. And every server there js / react and where it all goes. And finally, not a single argument was left to take.
I realized that support for the studio was done very quickly. In the end, everything exactly happened. I'm certainly hellishly pleased with this decision)
/// <reference path="../../references.d.ts"/> module DirectCrm { export interface SaveCustomFieldKindComponentProps extends Model<CustomFieldKindValueBackendViewModel> { } interface SaveCustomFieldKindComponentState { model?: CustomFieldKindValueBackendViewModel; validationContext: IValidationContext<CustomFieldKindValueBackendViewModel>; } export class SaveCustomFieldKindComponent extends React.Component<SaveCustomFieldKindComponentProps, SaveCustomFieldKindComponentState> { private _componentsMap: ComponentsMap<CustomFieldKindConstantComponentDataBase, CustomFieldKindTypedComponentProps>; constructor(props: SaveCustomFieldKindComponentProps) { super(props); this.state = { model: props.model, validationContext: createTypedValidationContext<CustomFieldKindValueBackendViewModel>(props.validationSummary) }; this._componentsMap = ComponentsMap.initialize(this.state.model.componentsMap); } _setModel = (model: CustomFieldKindValueBackendViewModel) => { this.setState({ model: model }); } _handleFieldTypeChange = (newFieldType: string) => { var clone = _.clone(this.state.model); clone.fieldType = newFieldType; clone.typedViewModel = { type: newFieldType, $type: this._componentsMap[newFieldType].viewModelType }; this._setModel(clone); } _getColumnPrefixOrEmptyString = (entityType: string) => { var entityTypeDto = _.find(this.props.model.entityTypes, et => et.systemName === entityType); return entityTypeDto && entityTypeDto.prefix || ""; } _hanleEntityTypeChange = (newEntityType: string) => { var clone = _.clone(this.state.model); clone.entityType = newEntityType; var columnPrefix = this._getColumnPrefixOrEmptyString(newEntityType); clone.columnName = `${columnPrefix}${this.state.model.systemName || ""}`; this._setModel(clone); } _handleSystemNameChange = (newSystemName: string) => { var clone = _.clone(this.state.model); clone.systemName = newSystemName; var columnPrefix = this._getColumnPrefixOrEmptyString(this.state.model.entityType); clone.columnName = `${columnPrefix}${newSystemName || ""}`; this._setModel(clone); } _renderComponent = () => { var entityTypeSelectOptions = this.state.model.entityTypes.map(et => { return { Text: et.name, Value: et.systemName } }); 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"> <FormGroup label=" " validationMessage={this.state.validationContext.getValidationMessageFor(m => m.entityType)}> <div className="form-control"> <Select value={this.state.model.entityType} options={entityTypeSelectOptions} width="normal" placeholder=" " onChange={this._hanleEntityTypeChange} /> </div> </FormGroup> <DataGroup label=" " value={this.state.model.columnName} /> <FormGroup label="" validationMessage={this.state.validationContext.getValidationMessageFor(m => m.name)}> <Textbox value={this.state.model.name} width="normal" onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.name)} /> </FormGroup> <FormGroup label=" " validationMessage={this.state.validationContext.getValidationMessageFor(m => m.systemName)}> <Textbox value={this.state.model.systemName} width="normal" onChange={this._handleSystemNameChange} /> </FormGroup> <FormGroup label=" " validationMessage={this.state.validationContext.getValidationMessageFor(m => m.fieldType)}> <div className="form-control"> <Select value={this.state.model.fieldType} options={fieldTypeSelectOptions} width="normal" placeholder=" " onChange={this._handleFieldTypeChange} /> </div> </FormGroup> <TypedComponent validationContext={this.state.validationContext.getValidationContextFor(m => m.typedViewModel)} onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.typedViewModel)} value={this.state.model.typedViewModel} constantComponentData={componentInfo.constantComponentData} /> <FormGroup> <Checkbox checked={this.state.model.isMultiple} label=" " onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.isMultiple)} disabled={false} /> </FormGroup> {this._renderShouldBeExportedCheckbox()} </div> </div>); } _getViewModelValue = () => { var clone = _.clone(this.state.model); clone.componentsMap = null; clone.entityTypes = null; return clone; } render() { return ( <div> <fieldset> {this._renderComponent() } </fieldset> <HiddenInputJsonSerializer model={this._getViewModelValue()} name={this.props.modelName} /> </div>); } _renderShouldBeExportedCheckbox = () => { if (this.state.model.entityType !== "HistoricalCustomer") return null; return ( <FormGroup validationMessage={this.state.validationContext.getValidationMessageFor(m => m.shouldBeExported)}> <Checkbox checked={this.state.model.shouldBeExported} label=" " onChange={getPropertySetter( this.state.model, this._setModel, viewModel => viewModel.shouldBeExported)} disabled={false} /> </FormGroup>); } } }
/// <reference path="../../libraries/underscore.d.ts"/> function getPropertySetter<TViewModel, TProperty>( viewModel: TViewModel, viewModelSetter: {(viewModel: TViewModel): void}, propertyExpression: {(viewModel: TViewModel): TProperty}): {(newPropertyValue: TProperty): void} { return (newPropertyValue: TProperty) => { var viewModelClone = _.clone(viewModel); var propertyName = getPropertyNameByPropertyProvider(propertyExpression); viewModelClone[propertyName] = newPropertyValue; viewModelSetter(viewModelClone); }; } function getPropertyName<TObject>(obj: TObject, expression: {(obj: TObject): any}): string { return getPropertyNameByPropertyProvider(expression); } function getPropertyNameByPropertyProvider(propertyProvider: Function): string { return /\.([^\.;]+);?\s*\}$/.exec(propertyProvider.toString())[1]; }
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; } }
@Html.ReactJsFor("DirectCrm.SaveCustomFieldKindComponent", m => m.Value)
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))); }
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 ); }); }
<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} /> ); } }
public class CustomFieldKindCreatePageViewModel : AdministrationSiteMasterViewModel { public CustomFieldKindCreatePageViewModel() { Value = new CustomFieldKindValueViewModel(); } [JsonBinded] public CustomFieldKindValueViewModel Value { get; set; } /// - }
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); // ... // , // ... if (result != null) { FillJsonBindedProperties(controllerContext, bindingContext, result); } return result; } private static string BuildFormVariableFullName(string modelName, string formVariableName) { return modelName.IsNullOrEmpty() ? formVariableName : string.Format("{0}.{1}", modelName, formVariableName); } private void FillJsonBindedProperties( ControllerContext controllerContext, ModelBindingContext bindingContext, object result) { var jsonBindedProperties = result.GetType().GetProperties() .Where(pi => pi.HasCustomAttribute<JsonBindedAttribute>()) .ToArray(); foreach (var propertyInfo in jsonBindedProperties) { var formFieldFullName = BuildFormVariableFullName( bindingContext.FallbackToEmptyPrefix ? string.Empty : bindingContext.ModelName, propertyInfo.Name); if (controllerContext.HttpContext.Request.Params.AllKeys.Contains(formFieldFullName)) { var json = controllerContext.HttpContext.Request.Params[formFieldFullName]; if (!json.IsNullOrEmpty()) { var convertedObject = DeserializeJson( json, propertyInfo.PropertyType, formFieldFullName, bindingContext, controllerContext); propertyInfo.SetValue(result, convertedObject); } } else { throw new InvalidOperationException( string.Format( " property {0} {1}. 99.9% js.", formFieldFullName, result.GetType().AssemblyQualifiedName)); } } } }
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); } }
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>); }
"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" } } }
public void PrepareForViewing(MvcModelContext mvcModelContext) { ComponentsMap = ModelApplicationHostController .Instance .Get<ReactComponentViewModelConfiguration>() .GetNamedObjectRelatedComponentsMapFor<CustomFieldKindTypedViewModelBase, CustomFieldType>( customFieldViewModel => customFieldViewModel.PrepareForViewing(mvcModelContext)); // - }
configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new StringCustomFieldKindTypedViewModel()); configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new IntegerCustomFieldKindTypedViewModel()); configuration.RegisterNamedObjectRelatedViewModel<CustomFieldKindTypedViewModelBase, CustomFieldType>( () => new EnumCustomFieldKindTypedViewModel());
{ "typedViewModel":{ "selectedEnumValues[0]":{ "systemName":[ " 250 " ] } }, "name":[ " " ] }
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>
private void RegisterEndUserInput( ISubmodelInputRegistrator<CustomFieldKindValueViewModel> registrator, CustomFieldKind customFieldKind) { // registrator.RegisterEndUserInput( customFieldKind, cfk => cfk.Name, this, m => m.Name); // }
public void Validate(ValidationContext validationContext) { // validationContext .Validate(this, cfk => cfk.Name) .ToHave(() => !Name.IsNullOrEmpty()) .OrAddError<CustomFieldCustomizationTemplateComponent>(c => c.NameRequired); // }
Source: https://habr.com/ru/post/276235/
All Articles