Unexpected href generated by Html.ActionLink - asp.net-mvc

I have this Html.ActionLink in my code:
#Html.ActionLink("Up",
"GetCategory",
"Category",
new { C_ID = CID, D_ID = DID, E_ID = EID, F_ID = FID },
new { id = "item-" + ItemID + "-getcategories-" + ItemLevel,
#class = "getCategory" })
I want to append a Javascript variable's value to the route dictionary. For this, I used the solution specified in this SO question ASP.NET MVC 2 Html.ActionLink with JavaScript variable
I attempted to customise the answer and this is what I have:
$(function ()
{
$('a.getCategory').click(function ()
{
.
.
.
var CatID = 0; //Calculate from this.ID
this.href = this.href + '?Cat_ID=' + CatID;
});
});
Thing is, the value of this.href is not the value generated by Html.ActionLink. Rather, it is the current URL of the page.
My understanding was this.href should refer to the ActionLink's href (since this refers to the anchor element generated by the ActionLink). What am I doing wrong?
EDIT
What is even more confusing: The Html.ActionLink is for one of the tabs (I'm using Jquery UI Tabs). So if I use .attr('href') to read the value in the JS function, I get a completely different value:
var AnchorHref = $(this).attr('href');
The value of AnchorHref is #ui-tabs-XX (where XX is a number) - this is a div auto-generated by Jquery UI tabs.

Related

Pass two parameters to controller, syntax error

I have a dropdown list. I want to click any item then redirect the view by parameters.
$(document).ready(function () {
$("#Languages").change(function () {
var selectedValue = $(this).find('option:selected').val();
window.location.href = "/Survey/SelectLanguage/" + selectedValue+"/id/"+#Model.CampaignGuid;
});
});
The action method in controller is:
public ActionResult SelectLanguage(string languageName, Guid id)
{
}
But I found in chrome debugger, there is an error.
Uncaught SyntaxError: Invalid or unexpected token
If I comment it out the code window.location.href = "/Survey/SelectLanguage/" + selectedValue+"/id/"+#Model.CampaignGuid;.
Then the error is gone. So what is wrong?
Change
window.location.href = "/Survey/SelectLanguage/" +
selectedValue + "/id/" + #Model.CampaignGuid;
to
window.location.href = "/Survey/SelectLanguage/" +
selectedValue + "/id/#Model.CampaignGuid";
If CampaignGuid is really a Guid, then you're writing out "/id/" + d0164584-bac0-485b-8a84-b7923caa6ee7 to your html, and Chrome's javascript engine doesn't know what d0164584-bac0-485b-8a84-b7923caa6ee7 is, it needs to be a string.
Well, since you do not have the url defined and you are using the default route, the default is based on query string. Try this:
$(document).ready(function () {
$("#Languages").change(function () {
var selectedValue = $(this).find('option:selected').val();
window.location.href = "/Survey/SelectLanguage/#Model.CampaignGuid?languageName=" + selectedValue;
});
});
By following the default route of asp.net mvc, you can pass an argument named id on the url as part of it, then you can use CampingGuid as id, and languageName you can use query string.
Try to define your url as
"/Survey/SelectLanguage?selectedValue=" + selectedValue + "&id=#Model.CampaignGuid"

Custom ValidationAttribute trigger on different client property change

I have built a custom validation attribute - LessThanDifference. Basically I give it two properties, and it checks to see if the value of the validated field is less than the difference of the two property names. Basically "is FieldC < (FieldA - FieldB)". That part works.
The IsValid function works fine, here is my client validation rules. (Bonus question - Is there any way to get the display name for firstoperand and secondoperand? It has the property name by attribute parameter.)
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "lessthandifference",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters.Add("firstoperand", FirstOperand);
rule.ValidationParameters.Add("secondoperand", SecondOperand);
yield return rule;
}
Where I'm totally lost is how to trigger the validation If A(firstoperand) or B(secondoperand) changes.
$.validator.addMethod(
'lessthandifference',
function (value, element, params) {
var firstVal = $('#' + params.firstoperand).val();
var secondVal = $('#' + params.secondoperand).val();
return (value <= (firstVal - secondVal));
});
$.validator.unobtrusive.adapters.add(
'lessthandifference', ['firstoperand', 'secondoperand'], function (options) {
var params = {
firstoperand: options.params.firstoperand,
secondoperand: options.params.secondoperand
};
options.rules['lessthandifference'] = params;
options.messages['lessthandifference'] = options.message;
//Set up Trigger?
});
I've tried to pass something like (#' + options.params.secondoperand) into another method, but have been unable to get the prop name for the base attribute (FieldC).
$().change seems like it would be the way to go if I could get it set right.
Thoughts?
I solved the main issue:
function addSecondaryValidatorCheck(mainElement, secondaryElement) {
$(secondaryElement).change(function () {
if ($(mainElement).val() > 0)
$(mainElement).valid();
});
And Implementation from the $.validator.unobtrusive.adapters.add function
addSecondaryValidatorCheck('#' + options.element.id, '#' + options.params.compareAttribute);
Still looking for a good way to pass the display name.

MVC Dynamically append img tag from controller

I want to add unknown number of images based on how many is there for specific user; i have used .Append function for div,labels,inputs and its work fine but when i want to append with source its not working !
so how can i do this ?
i have tried this:
` string sc = "$('<img/>', {src=\"~/Content/themes/img2.jpg\" alt=\"\",class:'table-wrapper'}).appendTo(finalDalata);";
return JavaScript(sc)`
finalDalata is my Div name
so whats iam doing wrong ?
Your appendTo code is invalid, it needs a selector, and your image attributes are not formatted properly, so it should be:
string sc = "$('<img/>', { src:'~/Content/themes/img2.jpg', alt:'', class:'table-wrapper'}).appendTo('#finalDalata');"
However, controller should return data and not control (exploits and injections comes to mind), so a better way would be to create an AppendImage function on the View side in javascript:
function AppendImage(attributes)
{
$('<img/>', attributes).appendTo('#finalDalata');
}
Note: you should probably sanitize the attributes before to be sure.
Then return a list of attributes only:
var images = new List<dynamic>();
// Do this for each images
images.Add(new { src="~/Content/themes/img2.jpg", alt="", class="table-wrapper"});
return Json(images);
And in the ajax success you can:
success: function (data) {
data.foreach( function (item) {
AppendImage(item);
});
}
Solved by TagBuilder
TagBuilder tag = new TagBuilder("img");
tag.Attributes.Add("id", "myImage");
tag.Attributes.Add("src", "/imgs/" + UserName + ".jpg");
tag.Attributes.Add("alt", "my image");
tag.Attributes.Add("class", "imgsClass");
then convert tag to string and pass it back to view
thnx

how to create dynamic url in collection and model using backbone

My collection and model like this:
detail_userid = 0;
detail_contactid = 0;
var ContactDetail = Backbone.Model.extend({
urlRoot: URL_CONTACTS1+detail_userid+"/"+detail_contactid
});
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: URL_CONTACTS1+detail_userid+"/"+detail_contactid
})
The entrance is:
ContactDetailManagePageModel.prototype.init = function(m,n){
detail_userid = m;
detail_contactid = n;
var myContactDetails = new ContactDetailCollection();
var contactDetailListView = new ContactDetailListView({
collection: myContactDetails
});
myContactDetails.fetch({reset:true});
}
But when it runs,the url is :http://localhost:8080/ws/users/contacts/0/0,it means that the assignment to detail_userid and detail_contactid is unsuccessful,I don't know why.
Hope for your help.Thanks.
I think you are statically definining the urlRoot and url properties before you are running the init of the PageModel (not quite sure where you are getting m and n from though...)
Both url and urlRoot can be a function, so you can pass in options during instantiation and have them dynamically set on the model.
Simple example covering defining the collection and then creating one
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: function(){
return URL_CONTACTS1 + this.options.detail_userid + "/" + this.options.detail_contactid;
}
});
var myContactDetails = new ContactDetailCollection({
detail_userid: foo,
detail_contactid: bar
});
As I mentioned, I'm not sure what your init function is doing, I'm guessing it's something custom from your app that I don't need to worry about.
I'm fairly sure the main thing to take away is to set url and urlRoot dynamically
I would fulfill the accepted answer with few remarks.
First parameter when initializing Backbone.Collection is array of models, then options. To create an empty collection with options you should do next
var c = new Backbone.Collection(null, {opt1: val1, opt2: val2});
Actually, you can't access this.options in url function, bec. there are no options like in a model. What you can do, is assign required properties from options upon initialization.
initialize: function (models, options) {
// `parseInt()` is used for consistency that `id` is numeric, just to be sure
this.detail_userid = parseInt(options.detail_userid);
this.detail_contactid = parseInt(options.detail_contactid);
}
Later you can access them like this:
url: function() {
return URL_CONTACTS1 + this.detail_userid + "/" + this.detail_contactid;
}
I wanted to use the HATEOAS href from one model to fetch data of another model. It worked to simply set the url on the newly created collection instead of defining it right away in the constructor.
var DailyMeasuresCollection = Backbone.Collection.extend({
//url : set dynamically with collection.url = url
model : DailyMeasuresModel,
parse : function(data) {
return data._embedded.dailyMeasures;
}
});
var DailyMeasuresTopicListItemView = Backbone.View.extend({
//...
events : {
'click .select-topic' : 'onClick'
},
onClick : function() {
var topicMeasures = new DailyMeasuresCollection()
topicMeasures.url = this.model.attributes._links.measures.href // <- here assign
var topicMeasuresView = new DailyMeasuresListView({
collection : topicMeasures
});
topicMeasures.fetch()
}
});

Change the Telerik DateTimePicker element ID, but keep the name

I have a ASP.NET MVC page with multiple forms on it, where each form edits a different instance of the same class. Each form also includes a Telerik DateTimePicker control. What I want is to have the element IDs change, but not the element name. So I have something like this:
string idString = string.Format("MyObject{0}", Model.ID)
#Html.Telerik().DatePickerFor(m => m.SomeDate).HtmlAttributes(new { id = idString + "-SomeDate" })
Now this works mostly fine, except that at the bottom of the page, the auto-generated Javascript that Telerik puts in looks like:
jQuery('#SomeDate').tDateTimePicker({
format:'M/d/yyyy h:mm tt',
minValue:new Date(1899,11,31,0,0,0,0),
maxValue:new Date(2100,0,1,0,0,0,0),
startTimeValue:new Date(2013,3,22,0,0,0,0),
endTimeValue:new Date(2013,3,22,0,0,0,0),
interval:30,
selectedValue:new Date(2013,3,22,11,9,1,180)
});
Note that my idString value didn't get put in. I can try:
#Html.Telerik().DatePickerFor(m => m.SomeDate).Name(idString + "SomeDate")
And this makes the auto-generated Javascript correct (jQuery('#MyObject123-SomeDate')) but now the element name is wrong, so TryUpdateModel fails.
How can I get this to work? Thanks.
EDIT: I hacked this using the following Javascript, but this is a kludgy way to fix this.
$(function () {
window.setTimeout(function () {
for (var i = 0; i < document.getElementsByTagName("input").length; i++) {
var obj = document.getElementsByTagName("input")[i];
if (obj.id.indexOf("SomeDate") == -1)
continue;
obj.name = "SomeDate";
}
}, 150)
});
The solution was to use the Telerik object's .InputHtmlAttributes method.
#Html.Telerik().DatePickerFor(m => m.SomeDate).InputHtmlAttributes(new { id = idString + "-SomeDate" })

Resources