private FlowFormField FieldFor <TProperty>(Expression <Func <TModel, TProperty> > expression, bool containsSection, FieldConfiguration fieldConfiguration = null) { /* * Renders a field for a non-boolean property of the model */ fieldConfiguration = fieldConfiguration ?? new FieldConfiguration(); if (fieldConfiguration.HideIfNull && fieldConfiguration.ReadOnly && GetValue(expression) == null) { return(null); } var htmlAttrs = Helper.ObjectToDictionary(fieldConfiguration.HtmlAttributes); var type = ElementType.Text; var grid = default(Grid); var selectList = default(MultiSelectList); var metadata = ModelMetadata.FromLambdaExpression(expression, _htmlHelper.ViewData); // Label var labelHtml = LabelHtml(expression, fieldConfiguration); if (fieldConfiguration.ReadOnly) { labelHtml = labelHtml.ReReplace(@"</?label.*?>", "", RegexOptions.IgnoreCase); } if (fieldConfiguration.ReadOnly && expression.ReturnType.Name != "Grid`1") { var value = (GetValue(expression) ?? "?"); var fi = value.GetType().GetField(value.ToString()); var attributes = fi != null ? (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false) : new DescriptionAttribute[] { }; var stringValue = (attributes.Length > 0) ? attributes[0].Description : value.ToString(); return(new FlowFormField(_writer, containsSection, labelHtml, (new HtmlString(stringValue)).ToString(), parentClass: fieldConfiguration.ParentClass)); } var validators = metadata.GetValidators(_htmlHelper.ViewContext.Controller.ControllerContext); var classes = new ClassBuilder(); if (htmlAttrs.ContainsKey("class")) { classes.Add((string)htmlAttrs["class"]); } if (metadata.IsRequired && (!metadata.ModelType.IsEnum || fieldConfiguration.Exclude == null || fieldConfiguration.Exclude.Length == 0)) { classes.Add("required"); } else { labelHtml = labelHtml.Replace("</label>", " <em>(Optional)</em></label>"); } switch (metadata.DataTypeName) { case "Digits": case "CreditCard": classes.Add(metadata.DataTypeName.Replace("-", "").ToLower()); break; case "IPAddress": classes.Add("ip-address"); break; case "MultilineText": type = ElementType.TextArea; break; } switch (metadata.ModelType.Name) { case "Double": classes.Add("number"); break; case "Int32": classes.Add("integer"); break; case "Email": classes.Add("email"); break; case "Password": type = ElementType.Password; break; case "Date": classes.Add("date"); break; case "HttpPostedFileBase": htmlAttrs["type"] = "file"; break; } if (metadata.ModelType.IsEnum && metadata.ModelType.IsDefined(typeof(FlagsAttribute), false)) { type = ElementType.Select; var list = (IEnumerable <object>)EnumInfo.CreateList(metadata.ModelType, fieldConfiguration.Exclude); var selectedValues = GetSelectedEnums((Enum)metadata.Model, fieldConfiguration.Exclude).ToList(); selectList = new MultiSelectList(list, "Value", "Display", selectedValues); } else if (metadata.ModelType.IsEnum || ( metadata.ModelType.IsGenericType && metadata.ModelType.GetGenericTypeDefinition().FullName == "System.Nullable`1" && metadata.ModelType.GetGenericArguments()[0].IsEnum ) || ( typeof(IEnumerable).IsAssignableFrom(metadata.ModelType) && !typeof(string).IsAssignableFrom(metadata.ModelType) && metadata.ModelType.GetGenericArguments()[0].IsEnum ) ) { type = ElementType.Select; var list = (IEnumerable <object>)EnumInfo.CreateList(metadata.ModelType.IsGenericType ? metadata.ModelType.GetGenericArguments()[0] : metadata.ModelType, fieldConfiguration.Exclude); var selectedValues = GetSelected(metadata.Model); selectList = new MultiSelectList(list, "Value", "Display", selectedValues); } foreach (var rule in validators.SelectMany(v => v.GetClientValidationRules())) { switch (rule.ValidationType) { case "range": classes.Add(string.Format("range([{0},{1}])", rule.ValidationParameters["min"], rule.ValidationParameters["max"])); break; case "min": classes.Add(string.Format("min({0})", rule.ValidationParameters["min"])); break; case "max": classes.Add(string.Format("max({0})", rule.ValidationParameters["max"])); break; case "regex": classes.Add(string.Format("match(/{0}/)", rule.ValidationParameters["pattern"])); break; case "length": case "maxlength": if (type == ElementType.Text || type == ElementType.Password) { htmlAttrs["maxlength"] = rule.ValidationParameters["max"]; } else { classes.Add(string.Format("maxlength({0})", rule.ValidationParameters["max"])); } break; case "minlength": classes.Add(string.Format("minlength({0})", rule.ValidationParameters["min"])); break; case "rangelength": classes.Add(string.Format("rangelength([{0},{1}])", rule.ValidationParameters["min"], rule.ValidationParameters["max"])); break; case "equalto": classes.Add(string.Format("equalTo('#{0}')", rule.ValidationParameters["other"].ToString().Split('.')[1])); labelHtml = labelHtml.Replace(" <em>(Optional)</em>", ""); break; case "existsin": type = ElementType.Select; selectList = GetSelectListFromCollection(expression, metadata, rule); break; case "grid": grid = GetGrid(expression, metadata, rule); break; case "notrequired": labelHtml = labelHtml.Replace(" <em>(Optional)</em>", ""); break; case "filetype": classes.Add(string.Format("accept('{0}')", rule.ValidationParameters["extension"])); fieldConfiguration.Tip = fieldConfiguration.Tip ?? string.Format("Filetype must be: {0}", rule.ValidationParameters["pretty-extension"]); break; } } if (classes.ToString().Trim() != "") { htmlAttrs["class"] = classes.ToString(); } if (fieldConfiguration.As.HasValue) { var validOverride = false; var overrideErrorMessage = ""; switch (type) { case ElementType.Select: switch (fieldConfiguration.As.Value) { case ElementType.Checkboxes: case ElementType.RadioButtons: case ElementType.Text: type = fieldConfiguration.As.Value; validOverride = true; break; } break; case ElementType.Text: switch (fieldConfiguration.As.Value) { case ElementType.Checkboxes: case ElementType.RadioButtons: case ElementType.Select: if (fieldConfiguration.PossibleValues != null) { selectList = fieldConfiguration.PossibleValues; type = fieldConfiguration.As.Value; validOverride = true; } else { overrideErrorMessage = "I was expecting a list."; } break; } break; } if (!validOverride) { throw new ApplicationException(string.Format("FieldConfiguration{{ As = {0} }} not valid for field: {1} (which defaults to {2}). {3}", fieldConfiguration.As.Value, expression, type, overrideErrorMessage)); } } var elementHtml = string.Empty; var errorHtml = string.Empty; var isValid = true; if (grid != null) { elementHtml = RenderGrid(grid, fieldConfiguration.ReadOnly); isValid = GetErrors(expression, out errorHtml); } else { switch (type) { case ElementType.Text: elementHtml = _htmlHelper.TextBoxFor(expression, htmlAttrs).ToHtmlString(); break; case ElementType.Password: elementHtml = _htmlHelper.PasswordFor(expression, htmlAttrs).ToHtmlString(); break; case ElementType.TextArea: elementHtml = _htmlHelper.TextAreaFor(expression, htmlAttrs).ToHtmlString(); break; case ElementType.Select: if (typeof(IEnumerable).IsAssignableFrom(metadata.ModelType) && !typeof(string).IsAssignableFrom(metadata.ModelType)) { elementHtml = _htmlHelper.ListBoxFor(expression, selectList, htmlAttrs).ToHtmlString(); } else { elementHtml = _htmlHelper.DropDownListFor(expression, selectList, fieldConfiguration.Label, htmlAttrs).ToHtmlString(); } break; case ElementType.Checkboxes: case ElementType.RadioButtons: // TODO: Use HTML Attributes var typeString = type == ElementType.Checkboxes ? "checkbox" : "radio"; elementHtml += HelperDefinitions.BeginInputList(typeString); elementHtml += string.Join("", selectList.Select(i => HelperDefinitions.InputListItem(typeString, expression.GetFieldName(), i.Value, i.Text, i.Selected).ToString())); elementHtml += HelperDefinitions.EndInputList(); break; } isValid = GetErrors(expression, out errorHtml); } elementHtml = (fieldConfiguration.Before ?? "") + elementHtml + (fieldConfiguration.After ?? ""); return(new FlowFormField(_writer, containsSection, labelHtml, elementHtml, errorHtml, isValid, fieldConfiguration.Hint, fieldConfiguration.Tip, fieldConfiguration.HideTip, fieldConfiguration.HintClass, fieldConfiguration.ParentClass, fieldConfiguration.DisplayFieldName)); }