Passing data attribute to TextBoxFor not working - asp.net

I have the following code inside my ASP.NET MVC 4 razor view:
<div>
<span class="f">Old Tag</span>
#Html.TextBoxFor(model => model.olfTag, new { data_autocomplete_source = Url.Action("AutoComplete", "Home") })
#Html.ValidationMessageFor(model => model.olfTag)
</div>
But data_autocomplete_source with TextBoxFor will not work. Can anyone give me some advice?

The following javascript should add the jquery autocomplete components to your control (and any control with the data-autocomplete-source attribute).
$(function() {
var createAutoComplete = function() {
var $input = $(this);
var options = {
source: $input.attr("data-autocomplete-source")
};
$input.autocomplete(options);
};
$("input[data-autocomplete-source]").each(createAutoComplete);
});

Related

Fluent Validation: Changing control style on error

I'm using Fluent Validation in my ASP.NET MVC application. I need to change the CSS class of the control when there's an error. Please see my code below.
using FluentValidation;
using Hrms.Framework.Common;
using Hrms.Web.Models;
namespace Hrms.Web.InputValidators
{
public class AnnouncementValidator : AbstractValidator<AnnouncementModel>
{
public AnnouncementValidator()
{
RuleFor(a => a.Title).NotEmpty().WithMessage("Announcement Title is required");
RuleFor(a => a.Title).MaximumLength(50).WithMessage(string.Format("Announcement Title should be 50 characters or less", 50));
}
}
}
Here is the code I have in the view (HTML)
#model Hrms.Web.Models.AnnouncementModel
#{
Layout = "~/Views/Shared/_Application.cshtml";
}
#using (Ajax.BeginForm("Save", "Announcement", new AjaxOptions
{
HttpMethod = "POST",
OnFailure = "saveFailed",
OnSuccess = "saveSucc",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result"
}))
{
#Html.AntiForgeryToken()
<div id="result"></div>
<label>#Resources.Organization.Announcement.Title</label>
#Html.TextBoxFor(m => m.Title, new { maxlength = #GlobalConstants.AnnouncementTitleMaxLength })
#Html.ValidationMessageFor(model => model.Title, null, new { #class = "errVal" })
<input type="submit" value="Save">
}
#section scripts {
<script type="text/javascript">
$(document).ready(function () {
$("#SendingDate").datepicker();
});
function saveSucc(m) {
$("#Title").val("");
$("#Body").val("");
$("#SendingDate").val("");
}
function saveFailed() {
alert("saveFailed");
}
</script>
}
Any help is highly appreciated
When I tried to edit this post, it showed an error saying that I cannot edit this post because it is mostly code, it asked me to add more details. And because there are no more details in my mind to add, I decided to add this text. I'm sorry...

Modals and form validation

I have a page that has about 4 different forms nested inside of their own modal pop up windows.
I have only ever done one page per form until this point.
What happens is when I submit to the controller and my modelState is not valid is that it routs back to my partial view.
How can I make the #validationFor messages show up in my modal, just like on my single form pages? Basically the page doesn't change, but if it does, it returns the page with the modal still up.
Will this require that I use some sort of Ajax call instead? are there any examples on here of that? I am pretty sure this is probably a duplicate thread, however I couldn't find any resources for myself.
Yes this will require some javascript to make this work.
create partial views to hold a form.
#using (Html.BeginForm("CreateHighSchoolType", "HighSchool", FormMethod.Post, new { id = "CreateHighSchoolTypeForm" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Create" id="CreateHighSchoolTypeButton" />
</p>
</fieldset>
}
create a controller methods to handle the partial view
public ActionResult CreateHighSchoolTypePartial()
{
return PartialView();
}
[HttpPost]
public ActionResult CreateHighSchoolTypePartial(LookupEditModel viewModel)
{
if (!ModelState.IsValid)
{
return PartialView(viewModel);
}
var hsType = (from t in _highSchoolTypeRepository.FindAll()
where t.Name == viewModel.Name
select t).FirstOrDefault();
if (hsType != null)
{
ModelState.AddModelError("Name", String.Format("This high school type already exists.", viewModel.Name));
return PartialView(viewModel);
}
_highSchoolTypeRepository.Save(new HighSchoolType
{
Name = viewModel.Name
});
return PartialView();
}
wire up everything with some jquery
the dialog opening
$("#AddHighSchoolTypeDialog").dialog({
position: 'center',
autoOpen: false,
modal: true,
resizable: false,
draggable: false,
title: "Add a new high school type",
open: function (event, ui) {
//Load the CreateAlbumPartial action which will return
// the partial view _CreateAlbumPartial
$(this).load("/HighSchoolType/CreateHighSchoolTypePartial", function () {
$("form#CreateHighSchoolTypeForm input:first").focus();
});
},
focus: function (event, ui) {
},
close: function (event, ui) {
$.ajax({
url: "/HighSchoolType/GetAllHighSchoolTypes",
type: "GET",
success: function (data) {
$("#HighSchoolTypeId option").remove();
$.each(data, function (key, value) {
$('#HighSchoolTypeId')
.append($("<option></option>")
.attr("value", key)
.text(value));
});
}
});
//$("#AddHighSchoolTypeDialog").remove();
},
width: 600
});
and the post
$("#CreateHighSchoolTypeButton").live('click', function () {
$.ajax({
url: "/HighSchoolType/CreateHighSchoolTypePartial",
type: "POST",
data: $("#CreateHighSchoolTypeForm").serialize(),
error: function (data) {
var errorMessage = $.parseJSON(data.responseText);
},
success: function (data) {
if (data) {
$('#AddHighSchoolTypeDialog').html(data);
}
else {
$('#AddHighSchoolTypeDialog').html('no data');
}
}
});
return false;
});
Note how on success your need to replace the html of the modal with what was returned from the post call.
As per my understanding, what you are doing is trying to have multiple forms in one page. You can try something like this -
#using (Html.BeginForm("Login", "Member", FormMethod.Post, new {})) {
#Html.LabelFor(m => m.LoginUsername)
#Html.TextBoxFor(m => m.LoginUsername)
#Html.LabelFor(m => m.LoginPassword)
#Html.TextBoxFor(m => m.LoginPassword)
<input type='Submit' value='Login' />
}
#using (Html.BeginForm("Register", "Member", FormMethod.Post, new {})) {
#Html.LabelFor(m => m.RegisterFirstName)
#Html.TextBoxFor(m => m.RegisterFirstName)
#Html.LabelFor(m => m.RegisterLastName)
#Html.TextBoxFor(m => m.RegisterLastName)
#Html.LabelFor(m => m.RegisterUsername)
#Html.TextBoxFor(m => m.RegisterUsername)
#Html.LabelFor(m => m.RegisterPassword)
#Html.TextBoxFor(m => m.RegisterPassword)
<input type='Submit' value='Register' />
}
inside a single page.
You can check answers on this link -
asp.net MVC 4 multiple post via different forms
The unobtrusive validation will only work on forms that exist on page load. if you are loading the pages dynamical in the modals you need to register them after they load
$.validator.unobtrusive.parse("#formid")
or you can just hide the views on load and show when needed

Fill dependent drop down list by using json

I have two drop down list that second one fill by on-change of first one.
I using json for ,but it doesn't work. here is my code:
<div class="col-md-6">
#Html.LabelFor(model => model.Counterparts.First().Name, new {#class = "control-label"})
#Html.DropDownListFor(m => m.CounterpartId, new SelectList(Model.Counterparts, "Id", "Name"), "select", new {#id = "SelectDepartment", onchange = "getData();"})
</div>
<div class="col-md-6">
#Html.LabelFor(model => model.Accounts.First().Name, new { #class = "control-label" })
#Html.DropDownListFor(m => m.AccountId, new SelectList(Model.Accounts, "Id", "Name"), "select", new { #class = "form-control" })
</div>
<script type="text/javascript" language="javascript">
function getData() {
var e = document.getElementById("SelectDepartment");
var counterpartid = e.options[e.selectedIndex].value;
alert('/ProcessCounterpart/GetCounterpartAccounts/' + counterpartid.toString());
$.getJSON('/ProcessCounterpart/GetCounterpartAccounts/' + counterpartid.toString(), function (data) {
alert(data);
});
}
</script>
public JsonResult GetCounterpartAccounts(int id)
{
var accounts = _accountService.GetAllAccountByCounterpartId(id);
return Json(accounts,JsonRequestBehavior.AllowGet);
}
I would personally suggest to use a selectize plugin. It has onchange event that fires each time you change the drop down. You can also fill options via ajax call which is what are you looking for. Here is an example:
$("#countriesDropDown").selectize({
load: function(query, callback) {
$.get("api/getCountries").done(function (data) {
if(data !== '') { callback(data) })
.fail(function(xmlHttpRequest, textStatus, errorThrown) {});
},
onChange: function(value) { loadCitylistByCountry(value); }
});
var $citiesSelectize = $("#citiesDropDown").[0].selectize;
function loadCitylistByCountry(value) {
$citiesSelectize.load(function(callback) {
$.get("api/getCitiesByCountry?coutryId=" + value).done(function (data) {
if(data !== '') { callback(data) })
.fail(function(xmlHttpRequest, textStatus, errorThrown) {});
});
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="countriesDropDown" type="text" />
<input id="citiesDropDown" type="text" />
Please note that this is just an example. This code might not even work. This is to only show you how you can use it. You can go to their site there you will find a lot of examples as well as the api.
Hope this will help
I suggest that you research ways on how to implement Cascading DropdownLists in MVC. There are many articles online such as:
Easiest way to create a cascade dropdown in ASP.NET MVC 3 with C#
http://www.c-sharpcorner.com/UploadFile/4d9083/creating-simple-cascading-dropdownlist-in-mvc-4-using-razor/
I'd go the JQuery route though; as it is the easiest one. The idea here is to populate both dropdowns and then use JQuery to hide or disable the options in the Child dropdown (i.e., the second one) based on selection in the Parent (i.e., the first one).

jCryption ASP.Net MVC

Our security team asked me to not submit plain text passwords in my log in page, we use HTTPS though. so I thought that I need to do client side encryption before submit, I searched for solution and decided to implement jCryption.
However the example presented there is PHP/python, after a few google found this. I did whatever was explained in the link but I don't know how to retrieve form data that user submitted in form.
I only see a key returns in my login post back action and the LoginModel that should contain username, password is null.
Login.cshtml
#model Portal.Model.Membership.LoginModel
#using jCryption
#{
Layout = null;
jCryption.HandleRequest(Request);
}
<html>
<head>
<script src="~/Assets/global/plugins/jquery.min.js" type="text/javascript"></script>
<script src="~/Assets/global/plugins/jquery-migrate.min.js" type="text/javascript"></script>
#jCryption.RenderScriptFor("form", src: #Url.Content("~/Assets/admin/scripts/jquery.jcryption.3.1.0.js"))
</head>
<body>
#using (Html.BeginForm(null, null, FormMethod.Post, new { #autocomplete = "off" }))
{
<div class="form-body">
<div class="form-group">
#Html.LabelFor(x => x.Username, new { #class = "placeholder" })
#Html.TextBoxFor(x => x.Username, new { #class = "form-input", autocomplete = "off" })
<span></span>
</div>
<div class="form-group">
#Html.LabelFor(x => x.Password, new { #class = "placeholder" })
#Html.PasswordFor(x => x.Password, new { #class = "form-input", autocomplete = "off" })
<span></span>
</div>
</div>
<div class="form-group">
<button id="btnLogin">Login</button>
</div>
}
</body>
<!-- END BODY -->
</html>
Update
I put break point on login post action and it popup twice, one with key and another with jCryption:
For MVC 5, you need to adjust a little bit.
at login.cshtml
#using jCryption
#{
jCryption.HandleRequest(Request);
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script src="/Scripts/jquery.jcryption.3.1.0.mod.js"></script>
<script type="text/javascript">
// tweak for compatibility with jquery.validate
(function($){
var _jCryption = $.jCryption;
var jCryptionMod = function(el,options){
var form = $(el), hasValidator = !!form.data('validator');
if (hasValidator){
var v = form.validate();
var prev_handler = v.settings.submitHandler;
v.settings.submitHandler = function (_form, event) {
if( prev_handler ) prev_handler.apply(this, arguments);
var form = $(_form);
if (!form.hasClass('jc-before-submit')) {
v.settings.submitHandler = prev_handler;
form.addClass('jc-before-submit');
setTimeout( function(){ form.trigger('_jc_submit', event); }, 100 );
}
};
_jCryption.call(this, form, $.extend(options, {
submitElement: form,
submitEvent: '_jc_submit',
beforeEncryption: function(){
form.removeAttr('disabled');// form element hack ( IE11 )
return true;
}
}));
} else {
return _jCryption.call(this,el,options);
}
}
$.extend(jCryptionMod, $.jCryption);
$.jCryption = jCryptionMod;
})(jQuery);
</script>
<script type="text/javascript">
$(document).ready(function(){
var form = $('form');
var url = form.attr('action') || '/Account/Login';
form.jCryption({
getKeysURL: url + '?getPublicKey=true',
handshakeURL: url + '?handshake=true'
});
});
</script>
}
AccountController, you should follow JakeJP's documentation (exact same code).
At IE F12 Developer Tools (Network-->go to detail view-->Request body), it showns &jCryptionKey= but not &UserName= and &Password=.
You lack the jCryptionHandler attribute in your action method. The attribute is responsible for handling the jCryption handshake and decryption.
[jCryptionHandler]
public ActionResult Login(LoginModel model)
{
return View();
}

Observables initialized/attached to Observable in extender not initialized at page load

I
I have created a text counter to tell the user how many characters of they have typed and how many they have remaining available. This should show when the text area has focus and disappear then the text area loses focus.
I have created a binding handler that uses an extender to extend the observable object that is being passed into it. The problem is that it works only after entering text, navigating off of the text area, and then navigating back to the text area.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div class="question" >
<label for="successes" data-textkey="successes">This is a question</label>
<textarea data-bind="textCounter: successes, hasFocus: successes.hasFocus, maxLength:200, event: { keyup:successes.updateRemaining }"></textarea>
<div class="lengthmessage edit" data-bind="visible:successes.hasFocus()">
<div >
<em>Length:</em> <span data-bind="text:successes.currentLength"></span>
<em>Remaining:</em> <span data-bind="text:successes.remainingLength"></span>
</div>
</div>
</div>
<script src="../Scripts/knockout-2.3.0.debug.js" type="text/javascript"></script>
<script type="text/javascript">
(function (ko) {
ko.extenders.textCounter = function (target, options) {
options = options || {};
options.maxLength = options.maxLength ? parseInt(options.maxLength) : 2000;
target.maxLength = ko.observable(options.maxLength);
target.currentLength = ko.observable(target().length);
target.remainingLength = ko.observable(target.maxLength() - target.currentLength());
target.hasFocus = ko.observable(false);
target.hasFocus.subscribe(function () {
target.currentLength(target().length);
target.remainingLength(target.maxLength() - target.currentLength());
});
target.updateRemaining = function (data, event) {
if (event.target == undefined && event.srcElement.value == "") {
target.currentLength(0);
}
else {
var e = $(event.target || event.srcElement);
target.currentLength(e.val().length);
if (target.currentLength() > target.maxLength()) {
e.val(e.val().substr(0, target.maxLength()));
target.currentLength(target.maxLength());
}
}
target.remainingLength(target.maxLength() - target.currentLength());
};
return target;
};
ko.bindingHandlers.textCounter = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var val = ko.utils.unwrapObservable(valueAccessor());
var observable = valueAccessor();
observable.extend({ textCounter: allBindingsAccessor() });
ko.applyBindingsToNode(element, {
value: valueAccessor()
});
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var val = ko.utils.unwrapObservable(valueAccessor());
var observable = valueAccessor();
ko.bindingHandlers.css.update(element, function () { return { hasFocus: observable.hasFocus }; });
}
};
var viewModel = function () {
this.successes = ko.observable("");
//this.successes.hasFocus = ko.observable();
}
ko.applyBindings(new viewModel());
} (ko));
</script>
</body>
</html>
If I uncomment:
//this.successes.hasFocus = ko.observable();
The page will behave the way that I want it to, from the very beginning, but it defeats the whole purpose of using the extender since my view model now has one of the objects from the extender in it.
I have got to believe that there is something relatively simple that I am missing here.
Thanks for your help..
The issue is that hasFocus has not been defined when the binding string here is parsed:
<textarea data-bind="textCounter: successes, hasFocus: successes.hasFocus, maxLength:200, event: { keyup:successes.updateRemaining }"></textarea>
So, when the binding string is parsed successes.hasFocus is undefined.
One option would be to apply the hasFocus binding inside of your textCounter binding after your hasFocus property is available.
Also, in Knockout 3.0 (released today), the parsing of the binding string happens when the value is accessed in the binding itself. So, your code actually works property in KO 3.0 already.

Resources