ASP.NET MVC 4 Html.Action with JavaScript variable - asp.net-mvc-4

I want to use javascript variable inside MVC Action route values. I referred this Stackoverflow post and the answer given there is working fine.
But i don't want to write an extra javascript function to achieve this. Without writing extra function, Is there any other way to do it. I meant, is there any new added feature in MVC4 for this? As the example in that link is for MVC 2.
self.EditUrl = ko.computed(function () {
return "#Url.Action(Actions.User_Update, Controllers.User, new { Id = self.Id() } )";
});

It is still the same case with MVC 4. You cannot mix client code and server code. I don't think it would even be possible in the future. Having said that, what you're trying to do is achievable. You can always write the url in a hidden field:
<input type="hidden" id="userUpdateUrl" value="#Url.Action("User_Update","User")"/>
Then use that on your client-side binding:
self.EditUrl = ko.computed(function () {
return $("#userUpdateUrl").val() + "?" + self.Id();
});

Related

Change WebGrid row color conditionally without Jquery/javascript ASP.NET MVC 4

I know this has been asked before but I wanted to ask it in my own way with more clarification. I am trying to conditionally set the background of a td that is created using a webGrid in ASP.NET MVC. I don't see a good way to do this.
So far what I have come up with is this:
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<div style=\"background-color: red\">Item Backordered</div>"
:
""
)),
This is an okay solution but I would like a more clean look because the webgrid default has a small padding in the table cell so the div won't expand completely to the size of the cell either.
Is there a way to edit the td in any way? I know I can change the background and other style attributes using jquery or javascript but I don't like the idea of having doing duplicate work to first build the table on the server, then on the client side iterate over it again conditionally changing the colors when this should have been completed with the first iteration.
Hope the following answer will help you
grid.GetHtml(columns: grid.Columns(grid.Column(columnName: "DATT", header: "Date",format: #<text> #{
if (#item.isCurrentlyBackordered)
{
<span>Item Backordered</span>
<script>
$("tr:contains('Item Backordered')").css("background-color", "yellow");
</script>
}
}</text>)))
Also you can write this in a common JQuery too
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<span>Item Backordered</span>"
:
""
)),
JQuery
<script type="text/javascript">
$(function () {
$("tr:contains('Item Backordered')").css("background-color", "yellow");
})
</script>
With the help of Golda's response and here
I was able to create an elegant solution. This solution uses JavaScript/JQuery, as it doesn't seem possible to do it without it but using (to me) a cleaner solution than what I had came across. What I did in the model class (type for List<>()) was add a property that refers to itself and returns an instance cast to its interface like so:
public iTrans This
{
get
{
return this;
}
}
I did this because the webGrid seems to only allow access to the properties and not methods; regardless of access level.
Then in that same model I have a method which will conditionally attach markup for a hidden input field to the data string and return it as an MvcHtmlString object:
public MvcHtmlString htmlColorWrapper(string cellStr, string hexColor = "#ccc")
{
if (isOnBackorder)
{
cellStr = cellStr + "<input type='hidden' class='color' value='" + hexColor + "'/>";
}
return new MvcHtmlString(cellStr);
}
And in the markup (partial view) I make my grid.Column call:
grid.Column("Date", header: "Date", format: (item) => item.This.htmlColorWrapper(item.Date.ToString("MM/dd/yyy"))),
Then I create the JavaScript function(s):
window.onload = function () {
SetFeaturedRow();
};
function SetFeaturedRow() {
$('.color').each(function (index, element) {
$(element).parent().parent().css('background-color', $(element).val());
});
}
The window.onload is needed to point to the SetFeaturedRow() function to set the row colors at page load, the function name, "SetFeaturedRow" is stored in the ajaxUpdateCallback property through the webgrid constructor arguments: new WebGrid(Model ..... ajaxUpdateCallback: "SetFeaturedRow"); Or it can be set through the WebGrid reference, ref.ajaxUpdateCallback = "SetFeatureRow"
This will be used during any ajax call the WebGrid class will make. So for example if there are multiple pages to the webgrid each selection is an ajax call and the row colors will need to be re-updated.
Hopefully this helps someone.

Angular Datatables: cannot call destroy/edit function

I cannot access the API methods mentioned in this doc, as well as the destroy method described here .
the HTML:
<table datatable dt-instance="dtInstance" dt-options="dtOptions" dt-columns="dtColumns" class="table-striped">
</table>
In my controller I have:
a.dtInstance = {};
var getTableData = function() {
var deferred = $q.defer();
deferred.resolve(tablecontent_array);
return deferred.promise;
};
a.dtOptions = DTOptionsBuilder.fromFnPromise( getTableData())
.withPaginationType('full_numbers');
a.dtColumns = [
DTColumnBuilder.newColumn('id').withTitle('ID').notVisible(), //
DTColumnBuilder.newColumn('groupby').withTitle('Group By').notSortable(),
DTColumnBuilder.newColumn('value').withTitle('value').notSortable()
]
'tablecontent_array' contains the data.
When I try to modify/destroy the table I get an error.
a.dtInstance.rerender()
Error message:
TypeError: a.dtInstance.rerender is not a function
My aim is to modify the table after certain user operations. The table data in rendering fine. But I cannot access any of its API methods.
May be I am doing some mistake while doing so, I am new to angular-datatable, any help/suggestions regarding this issue will be helpful.
After dwelling into the question for days I tried creating plnkr as suggested in the comment. It turns out I was not using the correct versions of datatables, Angular and host of other Libraries. Corrected that and the issue was resolved.
Even then my objective of adding/appending new column(s) was not resolved in angular way. However some folks posted possible solutions using jquery datatables.

How to use store.filter / store.find with Ember-Data to implement infinite scrolling?

This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.

ASP MVC 4 Ajax.ActionLink for delete using AntiForgeryToken

I have a number of index pages where the row has an actionlink like so
#Ajax.ActionLink("Delete", "Delete", "AdverseEvent", new { id = Model.AdverseEventId }, new AjaxOptions { HttpMethod = "Post",OnSuccess = "rowDeleted", LoadingElementId="ajaxRequest_processing", Confirm = String.Format("Are you sure you want to delete adverse event for participant {0} at {1} ?", Model.ParticipantId, Model.EventTime) }, new { #class = "deleteAction" })
An actionlink is a great way to use progressive enhancement, because of course there is also a delete action, with get and post methods to perform the delete for those with javascript disabled.
I need to add an AntiForgeryToken. For an Ajax.BeginForm helper, Jon White's code works beautifully:
$.ajaxPrefilter(function (options, localOptions, jqXHR) {
var type = options.type.toLowerCase();
if (type === 'post') {
var token = GetAntiForgeryToken();
jqXHR.setRequestHeader(token.name, token.value);
}
});
When this gets executed within an actionlink, I assume because the index table is not wrapped in a form, I get the error message:
The required anti-forgery form field "__RequestVerificationToken" is not present
So i could wrap the whole table in a form posting back to the delete action, but this is then not very neat if I want to use other ajax.actionlinks to different actions within the table. I could wrap each actionlink in its own form, each with its own antiforgery token, but this is a significant amount of extra markup, and will leave dozens of elements on the page with identical values and name. The other option would be to use the ActionLink OnBegin method to wrap the button in a form, but the unobtrusive ajax library does not seem to pass any reference to the element causing the ajax get/post (foolishly in my opinion - you can upvote this issue on codeplex).
Any thoughts on a neat solution? Thank you.
You can add the token into the page and then use Ajax to send the field over in another call.
see How to include the #Html.AntiForgeryToken() when deleting an object using a Delete link

implement signalR without jquery

is it possible to implement SignalR without the use of Jquery. I want to create a module for Titanium, but I don't know how dependent SignalR is on the DOM. Is jQuery used just for the ajax request? how hard do you think this would be?
Um its not impossible but it'll be abit of work. you will basicly need to re-write all jquery syntax ($...) in
Jquery.signalR.js
as regual javascript. Also you will only be able to do low level connections as the "hub" model also requires jquery.
You will probably need to include JSON.js so you can make your ajax call like this.
var the_object = {};
var http_request = new XMLHttpRequest();
http_request.open( "POST", url + "/negotiate, true );
...
http_request.onreadystatechange = function () {
if ( http_request.readyState == 4 && http_request.status == 200 ) {
the_object = JSON.parse( http_request.responseText );
}
};
http_request.send(null);