public static HtmlString ngDropDownListFor <TModel, TProperty, TDisplayProperty>(this IHtmlHelper <TModel> html, Expression <Func <TModel, TProperty> > propertyExpression, Expression <Func <TModel, TDisplayProperty> > displayExpression, string source, string nullOption, IDictionary <string, object> htmlAttributes) { var helper = html as AngularHtmlHelper <TModel>; if (helper == null) { throw new InvalidOperationException("You need to configure the services container to return AngularHtmlHelper<T> for IHtmlHelper<T>."); } var propertyExpressionText = ExpressionHelper.GetExpressionText(propertyExpression); var displayExpressionText = ExpressionHelper.GetExpressionText(displayExpression); var metadata = ExpressionMetadataProvider.FromLambdaExpression(propertyExpression, helper.ViewData, helper.ModelMetadataProvider); var tag = new TagBuilder("select"); var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyExpressionText); var displayFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(displayExpressionText); var displayFieldNameParts = displayFieldName.Split('.'); displayFieldName = displayFieldNameParts[displayFieldNameParts.Length - 1]; tag.Attributes["id"] = helper.GetFullHtmlFieldId(propertyExpressionText).ToString(); tag.Attributes["name"] = valueFieldName; tag.Attributes["ng-model"] = valueFieldName; var ngOptionsFormat = "a.{0} as a.{1} for a in {2}"; var ngOptions = string.Format(ngOptionsFormat, valueFieldName, displayFieldName, source); tag.Attributes["ng-options"] = ngOptions; if (nullOption != null) { var nullOptionTag = new TagBuilder("option"); nullOptionTag.Attributes["value"] = string.Empty; nullOptionTag.SetInnerText(nullOption); tag.InnerHtml = nullOptionTag.ToString(); } var clientValidators = helper.GetClientValidators(null, metadata); var isRequired = clientValidators.SingleOrDefault(cv => string.Equals(cv.ValidationType, "required", StringComparison.OrdinalIgnoreCase)) != null; if (isRequired) { tag.Attributes["required"] = string.Empty; } tag.MergeAttributes(htmlAttributes, replaceExisting: true); return(tag.ToHtmlString(TagRenderMode.Normal)); }
public static HtmlString ngTextBoxFor <TModel, TProperty>(this IHtmlHelper <TModel> html, Expression <Func <TModel, TProperty> > expression, IDictionary <string, object> htmlAttributes) { var expressionText = ExpressionHelper.GetExpressionText(expression); var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider); var ngAttributes = new Dictionary <string, object>(); ngAttributes["type"] = "text"; // Angular binding to client-side model (scope). This is required for Angular validation to work. var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText); ngAttributes["name"] = valueFieldName; ngAttributes["ng-model"] = valueFieldName; // Set input type if (string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.EmailAddress), StringComparison.OrdinalIgnoreCase)) { ngAttributes["type"] = "email"; } else if (metadata.ModelType == typeof(Uri) || string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.Url), StringComparison.OrdinalIgnoreCase) || string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.ImageUrl), StringComparison.OrdinalIgnoreCase)) { ngAttributes["type"] = "url"; } else if (IsNumberType(metadata.ModelType)) { ngAttributes["type"] = "number"; if (IsIntegerType(metadata.ModelType)) { ngAttributes["step"] = "1"; } else { ngAttributes["step"] = "any"; } } else if (metadata.ModelType == typeof(DateTime)) { if (string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.Date), StringComparison.OrdinalIgnoreCase)) { ngAttributes["type"] = "date"; } else if (string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.DateTime), StringComparison.OrdinalIgnoreCase)) { ngAttributes["type"] = "datetime"; } } // Add attributes for Angular validation var clientValidators = html.GetClientValidationRules(metadata, null); foreach (var validator in clientValidators) { if (string.Equals(validator.ValidationType, "length")) { if (validator.ValidationParameters.ContainsKey("min")) { ngAttributes["ng-minlength"] = validator.ValidationParameters["min"]; } if (validator.ValidationParameters.ContainsKey("max")) { ngAttributes["ng-maxlength"] = validator.ValidationParameters["max"]; } } else if (string.Equals(validator.ValidationType, "required")) { ngAttributes["required"] = null; } else if (string.Equals(validator.ValidationType, "range")) { if (validator.ValidationParameters.ContainsKey("min")) { ngAttributes["min"] = validator.ValidationParameters["min"]; } if (validator.ValidationParameters.ContainsKey("max")) { ngAttributes["max"] = validator.ValidationParameters["max"]; } } else if (string.Equals(validator.ValidationType, "equalto")) { // CompareAttribute validator var fieldToCompare = validator.ValidationParameters["other"]; // e.g. *.NewPassword var other = validator.ValidationParameters["other"].ToString(); if (other.StartsWith("*.")) { // The built-in CompareAttributeAdapter prepends *. to the property name so we strip it off here other = other.Substring("*.".Length); } ngAttributes["app-equal-to"] = other; // TODO: Actually write the Angular directive to use this } // TODO: Regex, Phone(regex) } // Render! if (metadata.Model != null) { ngAttributes.Add("value", metadata.Model.ToString()); } var tag = new TagBuilder("input"); tag.MergeAttributes(MergeAttributes(ngAttributes, htmlAttributes)); return(tag.ToHtmlString(TagRenderMode.SelfClosing)); }