MusicStore.Spa changes:

- Make the account manage page work
- Changed ngTextBoxFor extension not use the base TextBoxFor helper as it was adding unwanted client validation attributes
- Fix controller route
This commit is contained in:
DamianEdwards 2014-06-16 18:09:08 -07:00
parent 4a4fe86df4
commit 7ec9c71181
5 changed files with 36 additions and 26 deletions

View File

@ -99,9 +99,8 @@ namespace MusicStore.Controllers
// //
// GET: /Account/Manage // GET: /Account/Manage
//Bug: https://github.com/aspnet/WebFx/issues/339
[HttpGet] [HttpGet]
public IActionResult Manage(ManageMessageId? message) public IActionResult Manage(ManageMessageId? message = null)
{ {
ViewBag.StatusMessage = ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."

View File

@ -12,14 +12,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
{ {
return html.ngTextBoxFor(expression, new RouteValueDictionary { { "type", "password" } }); return html.ngPasswordFor(expression, null);
} }
public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{ {
return html.ngTextBoxFor(expression, MergeAttributes( return html.ngPasswordFor(expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
new RouteValueDictionary { { "type", "password" } },
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)));
} }
public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) public static HtmlString ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
@ -51,8 +49,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, helper.ViewData, helper.ModelMetadataProvider); var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, helper.ViewData, helper.ModelMetadataProvider);
var ngAttributes = new Dictionary<string, object>(); var ngAttributes = new Dictionary<string, object>();
ngAttributes["type"] = "text";
// Angular binding to client-side model (scope). This is required for Angular validation to work. // Angular binding to client-side model (scope). This is required for Angular validation to work.
ngAttributes["ng-model"] = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText); var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
ngAttributes["name"] = valueFieldName;
ngAttributes["ng-model"] = valueFieldName;
// Set input type // Set input type
if (string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.EmailAddress), StringComparison.OrdinalIgnoreCase)) if (string.Equals(metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.EmailAddress), StringComparison.OrdinalIgnoreCase))
@ -139,7 +141,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
// Render! // Render!
return html.TextBoxFor(expression, null, MergeAttributes(ngAttributes, htmlAttributes)); 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);
} }
//private static bool IsNumberType(Type type) //private static bool IsNumberType(Type type)
@ -255,7 +264,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
tag.MergeAttributes(htmlAttributes, replaceExisting: true); tag.MergeAttributes(htmlAttributes, replaceExisting: true);
return html.Raw(tag.ToString()); return tag.ToHtmlString(TagRenderMode.Normal);
} }
public static HtmlString ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string formName) public static HtmlString ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string formName)
@ -341,6 +350,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
private static IDictionary<string, object> MergeAttributes(IDictionary<string, object> source, IDictionary<string, object> target) private static IDictionary<string, object> MergeAttributes(IDictionary<string, object> source, IDictionary<string, object> target)
{ {
if (target == null)
{
return source;
}
// Keys in target win over keys in source // Keys in target win over keys in source
foreach (var pair in source) foreach (var pair in source)
{ {

View File

@ -78,7 +78,7 @@ namespace MusicStore.Spa
routes.MapRoute(null, "api/albums/mostPopular", new { controller = "AlbumsApi", action = "MostPopular" }); routes.MapRoute(null, "api/albums/mostPopular", new { controller = "AlbumsApi", action = "MostPopular" });
routes.MapRoute(null, "api/albums/all", new { controller = "AlbumsApi", action = "All" }); routes.MapRoute(null, "api/albums/all", new { controller = "AlbumsApi", action = "All" });
routes.MapRoute(null, "api/albums/{albumId}", new { controller = "AlbumsApi", action = "Details" }); routes.MapRoute(null, "api/albums/{albumId}", new { controller = "AlbumsApi", action = "Details" });
routes.MapRoute(null, "{controller}/{action}", new { controller = "Home", action = "Index" }); routes.MapRoute(null, "{controller}/{action}/{id?}", new { controller = "Home", action = "Index" });
}); });
} }
} }

View File

@ -2,9 +2,11 @@
//TODO: Until we have a way to specify the layout page at application level. //TODO: Until we have a way to specify the layout page at application level.
Layout = "/Views/Shared/_Layout.cshtml"; Layout = "/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Manage Account"; ViewBag.Title = "Manage Account";
ViewBag.ngApp = "MusicStore.Store";
} }
<h2>@ViewBag.Title.</h2> <h2>@ViewBag.Title.</h2>
<p class="text-success">@ViewBag.StatusMessage</p> <p class="text-success">@ViewBag.StatusMessage</p>
<div class="row"> <div class="row">
@ -14,8 +16,9 @@
</div> </div>
@section Scripts { @section Scripts {
@*TODO : Until script helpers are available, adding script references manually*@ <script src="~/js/angular.js"></script>
@*@Scripts.Render("~/bundles/jqueryval")*@ <script src="~/js/angular-route.js"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script> @* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script> for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
<script src="~/js/MusicStore.Store.js"></script>
} }

View File

@ -1,18 +1,14 @@
@using System.Security.Principal @using System.Security.Principal
@model MusicStore.Models.ManageUserViewModel @model MusicStore.Models.ManageUserViewModel
<p>You're logged in as <strong>@Context.HttpContext.User.Identity.GetUserName()</strong>.</p> <p>You're logged in as <strong>@User.Identity.GetUserName()</strong>.</p>
@using (Html.BeginForm("Manage", "Account", FormMethod.Post, @using (Html.BeginForm("Manage", "Account", FormMethod.Post,
new new { @class = "form-horizontal",
{ role = "form", novalidate = "", name = "changePassword",
@class = "form-horizontal", app_prevent_submit = "changePassword.$invalid",
role = "form", ng_submit = "changePassword.submitAttempted=true" }))
novalidate = "",
name = "changePassword",
app_prevent_submit = "changePassword.$invalid",
ng_submit = "changePassword.submitAttempted=true"
}))
{ {
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
<h4>Change Password</h4> <h4>Change Password</h4>
@ -60,6 +56,4 @@
<button type="submit" class="btn btn-primary">Change password</button> <button type="submit" class="btn btn-primary">Change password</button>
</div> </div>
</div> </div>
}
} }