I am loading server response in a datatable using js in a asp.net core razor page. Because the data/UI is complex, I need to render different layouts based on current value in each table cell.
Datatable supports a renderer function for each cell, so I could have something like:
...
"data":"somefield",
"renderer":function(data,type,row,meta){
if (data.someId)=="someValue"{
return "<div... some label with somValue</div>"
}else{
return "<div... some label without value</div>"
}
}
}
This works perfectly fine, however when divs get complex with style and many labels it becomes harder to maintain or change.
I did look a bit into Razor's PartialViews as it may seem like a good alternative. Having the UI in a cshtml file, being able to pass parameters from parent #Model and using c# in it to render it based on the parameter received.
While I am able to load the partial view in the parent page, using <partial name=''/> or #Html.Partial(...) I didn't manage to get it's content in js using $.get and return it in the datatable's render function. Probably async wouldn't work in this case? Or it would be too slow?
My question is: what would be a better way to handle this situation? Maybe partial views are not the way. I am looking for a way of easily maintaining/changing the cell content. Thank you for your time.
I'm not really familiar with asp.net but I will try to answer your question.
I don't think PartialViews are going to work in the way you suggest because they appear to be server-side code, and trying to do a GET request for every line in your table could potentially generate a large number of server requests.
I think you have a couple of potential solutions. First is to loop through your data on the server, and for each property that matches the condition generate the partial view and assign it to the property. Then return your data array with one of the properties in each row being a chunk of HTML. As I said, I don't have experience with this language so it's hard for me to provide a code example, but I hope you understand what I'm trying to say. Then in DataTables you just need to output the value
columns: [
{ data: 'someField' }
]
THe second option is to generate the HTML on the client using JavaScript. Since you say that it could be complex it's best if you have a function that returns a HTML string. If it's a large amount of HTML then you could even put this function in a separate file and export it, to make it more manageable. There are a couple of typos in your example, so I'm going to fix them here. Renderer is a table property, the one you want is columns.render. Also in the render function the data argument references the data property that is defined in the line above. If you want to reference a different property, use the row argument.
columns: [
{
data: 'someField',
render: (data, type, row) => renderMyData(data, row)
}
]
function renderMyData(data, row) {
if (row.someId == "someValue") {
return "<div...> some label with somValue</div>"
} else {
return "<div...> some label without value</div>"
}
}
Related
I'm building a travel website with Ruby on Rails 4 that makes heavy use of Javascript (or Coffeescript) for Google Maps and other APIs. This involves making a single call to the server, creating a javascript object with the results, then immediately rendering some HTML. A bit later, I will need to render different HTML using the same data.
A typical use case might be:
User searches for transportation between two different destinations
Coffeescript sends ajax post request to rails server
Rails server returns a JSON object with the results. Let's call this searchResults, which is an array of routes (e.g. searchResults['routes'][0]['path'] or searchResults['routes'][0]['price'])
The application immediately renders the results of this search as HTML (format 1)
Later, based on user action, the application must render data about one of the routes in the search result. This action requires rendering of different HTML than in step 4 (format 2).
Currently, in Step 3, I'm creating an instance of a SearchResults class in Coffeescript:
#holds all of the information for a single the transporation search call
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, oName, dName) ->
#origin = oName
#destination = dName
response = #search(oLat, oLng, dLat, dLng).responseJSON
#longestRoute = response.longestRoute #in minutes
#routes = response.routes
The reason I'm creating a Coffeescript class is because I'm trying to avoid hitting the server again. That is slow and I have an API limits to consider. My question is about steps 4 and 5. I've come across two different methods of doing what I need and wondering what the implications of each on speed/performance are.
Method 1: Cloning Hidden Div
I have methods in TransportationSearch that clone a hidden div, set the attributes, and insert it into the DOM:
renderFormatOne: ->
for route in routes
content = $('.div-one-template').clone().removeClass('hidden')
#...sets content for template. For example:
content.find('.price').html(route['price'])
#Insert template into Dom
$('#results-one').append(content)
renderFormatTwo: ->
...
Method 2: Using AJAX/Rails to Render the HTML
The other approach is to have house the HTML templates in a Rails partial, then use AJAX to send data to the controller and render the result.
Coffeescript:
#sets the content of the side-menu with the HTML from transportation call
showTransportation: (searchResults) =>
#first, get HTML
$.ajax '/segments/side_menu_transportation',
type: 'post'
data:
searchResults: JSON.stringify(searchResults)
success: (data) ->
$('#add-transport-box').html(data)
return true
error: ->
alert 'passDestinationToTransportation Unsuccessful'
return
#show()
Controller:
def side_menu_transportation
#searchResults = JSON.parse(params[:searchResults])
render partial: 'trips/transport_search'
end
Method 1 seems a little sloppy to me as it places a lot of the HTML structure in Coffeescript. However, speed is my priority and will probably dictate my decision. I'd prefer to use Method 2, but I'm wondering if the AJAX POST request is slow even if I'm not hitting my rails server.
Please let me know the speed / performance implications of these approaches, or if I'm missing something totally obvious :D.
Thanks in advance!
I don't think you should be sending data back to server to generate some HTML - if you do wouldn't that be generating frontend on the backend? Sounds a little bit odd to me. And it is a no-no from UX point of view because of lower responsiveness of the UI.
The speed of Javascript should not be a concern for you. Angular for example renders HTML all the time and unless developer was really sloppy, the impact on browser is not that big.
The HTML in Javascript. Well... again, this is frontend, you can't avoid it that much. But what might work for you is to have interpolated templates rather than copying, traversing and modifying DOM nodes. Just like this: <some><markup> #{route.price} </markup><some>. Having it that way would (possibly) reduce number of DOM operations (esp. costly traversing and lookup) and also would define body of the templates, so you see them full, as they are in one place.
Btw, it looks like I described what an Angular directive is with this paragraph - did you try to investigate it?
If you really need to render HTML server side (I advise you to not, but maybe I'm missing something) please don't make user wait for it - how about to render both/few templates at the same time of first call?
Lastly a hint:
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, oName, dName) ->
#origin = oName
#destination = dName
is equal to
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, #origin, #destination) ->
I am relatively new to Meteor, and I'm trying to create a web store for my sister-in-law that takes data from her existing Etsy store and puts a custom skin on it. I've defined all of my Meteor.methods to retrieve the data, and I've proofed the data with a series of console.log statements... So, the data is there, but it won't render on the screen. Here is an example of some of the code on the server side:
Meteor.methods({
...
'getShopSections': function() {
this.unblock();
var URL = baseURL + "/sections?api_key="+apiKey;
var response = Meteor.http.get(URL).data.results;
return response;
}
...
});
This method returns an array of Object. A sample bit of JSON string from one of the returned Objects from the array:
{
active_listing_count: 20,
rank: 2,
shop_section_id: 1******0,
title: "Example Title",
user_id: 2******7
}
After fetching this data without a hitch, I was ready to make the call from the client side, and I tried and failed in several different ways before a Google search landed me at this tutorial here: https://dzone.com/articles/integrating-external-apis-your
On the client side, I have a nav.js file with the following bit of code, adapted from the above tutorial:
Template.nav.rendered = function() {
Meteor.call('getShopSections', function(err, res) {
Session.set('sections', res);
return res;
});
};
Template.nav.helpers({
category: function() {
var sections = Session.get('sections');
return sections;
}
});
And a sample call from inside my nav.html template...
<ul>
{{#each category}}
<li>{{category.title}}</li>
{{/each}}
</ul>
So, there's a few things going on here that I'm unsure of. First and foremost, the DOM is not rendering any of the category.title String despite showing the appropriate number of li placeholders. Secondly, before I followed the above tutorial, I didn't define a Session variable. Considering that the list of shop categories should remain static once the template is loaded, I didn't think it was necessary from what I understand about Session variables... but for some reason this was the difference between the template displaying a single empty <li> tag versus a number of empty <li>'s equal to category.length --- so, even though I can't comprehend why the Session variable is needed in this instance, it did bring me one perceived step closer to my goal... I have tried a number of console.log statements on the client side, and I am 100% sure the data is defined and available, but when I check the source code in my Developer Tools window, the DOM just shows a number of empty li brackets.
Can any Meteor gurus explain why 1) the DOM is not rendering any of the titles, and 2) if the Session variable indeed necessary? Please let me know if more information is needed, and I'll be very happy to provide it. Thanks!
You set the data context when you use #each, so simply use:
<li>{{title}}</li>
If a Session is the right type of reactive variable to use here or not is hard to determine without knowing what you are doing but my rough guess is that a Mini Mongo collection may be better suited for what it appears you are doing.
To get you started on deciding the correct type of reactive variable to use for this head over to the full Meteor documentation and investigate: collections, sessions, and reactive vars.
Edit: To step back and clarify a bit, a Template helper is called a reactive computation. Reactive computations inside of helpers will only execute if they are used in their respective templates AND if you use a reactive variable inside of the computation. There are multiple types of reactive variable, each with their own attributes. Your code likely didn't work at all before you used Session because you were not using a reactive variable.
A week ago I ran into a problem with emberjs and DataTables.
I was using ember-data to get data from the asp codebehind using webmethods based on the route parameters. Then I would use that data to create a table with datatables. However, when I changed the route, which changed the data and therefore changed the html, datatables would add the rows, but it wouldn't remove the old rows. In addition none of the functionality would work on the new rows and whenever I would sort, it would remove the new data.
Please let me know if anyone has a better answer than the one I posted.
I looked and found a lot of questions on this topic, or similar topics; However all of the solutions were hacky or costly performance-wise. So I found my own.
It isn't perfect; I would love for ember to implement an event based on this.
I added a controller initially for navigating in my application view. The change event looks mostly like this:
paramsChanged: function () {
if (this.type && this.filingType.value && this.year && this.period) {
this.transitionToRoute('application');
Ember.run.next(this, function () {
this.transitionToRoute(this.type.value, this.year, this.period);
});
//console.log('persist');
}
}.observes('type', 'year', 'period')
This is changing the route to application(basically removing the sub view) then moving to whichever route I need next.
The performance cost, although untested, should be negligible. I need to run the code for creating the view anyway, and I'm already in the application. I'm destroying a little bit extra by transitioning to the index, then I'm recreating the sub-view on the next run loop causing the initialization code contained in didInsertElement to be run.
I've inherited a project which uses Knockout.JS to render a listing of posts. The client has asked that this listing be paginated and I'm wondering if this is possible and appropriate using Knockout.JS. I could easily achieve this in pure JavaScript but I'd like to use Knockout (if appropriate) for consistency.
From what I can tell, the page uses a Native Template in the HTML of the page. There is a ViweModel which stores the posts in a ko.ObservableArray() and a post model.
The data is loaded via a jQuery ajax call where the returned JSON is mapped to post model objects and then passed into the ObservableArray which takes care of the databinding.
Is it possible to amend the ViewModel to bind pagination links (including "previous" and "next" links when required) or would I be better off writing this in plain JS?
It should be easy enough to build a computed observable in knockout that shows a "window" of the full pagelist. For example add to the view model:
this.pageIndex = ko.observable(1);
this.pagedList = ko.computed(function() {
var startIndex = (this.pageIndex()-1) * PAGE_SIZE;
var endIndex = startIndex + PAGE_SIZE;
return this.fullList().slice(startIndex, endIndex);
}, this);
Then bind the "foreach" binding showing the record to pagedList instead of the full list, and in the forward and back links, simply change the value of pageIndex. Starting from there, you should be able to make it more robust/provide more functionality.
Also, this assumes you preload all data to the client anyway. It's also possible to make JSON calls on the previous and next link and update the model with the returned items. The "next" function (to be added to the view model prototype), could look like this:
ViewModel.prototype.next = function() {
var self = this;
this.pageIndex(this.pageIndex()+1);
$.ajax("dataurl/page/" + this.pageIndex(), {
success: function(data) {
self.dataList(data);
}
});
}
(using jQuery syntax for the ajax call for brevity, but any method is fine)
Writing features in KO always tend to generate less code and cleaner code than doing the same in "plain JS", jQuery or similar. So go for it!
I implemented a combobox with paging like this
https://github.com/AndersMalmgren/Knockout.Combobox/blob/master/src/knockout.combobox.js#L229
In my blog post, I have explained in very detail how to do it. you can find it (here. http://contractnamespace.blogspot.com/2014/02/pagination-with-knockout-jquery.html). It's very easy to implement and you can do it with a simple JQuery plugin.
Basically, I have used normal knockout data binding with AJAX and after data has been retrieved from the server, I call the plugin. You can find the plugin here. its called Simple Pagination.
I have a big problem with using Knockout JS. In my view model I have a field, called Method, that is actually an other view model.
This view model can be one of three different things (it is mapped to a polymorphic object in the domain model). To solve this I use templates that checks which type of Method that is selected withing the domain model and then shows the template that binds data for that type.
The function that checks the type of method looks like:
this.getTemplate = function (data) {
var method = data.original.get_Method();
if (method instanceof MyProj.MethodA)
return "methodA";
else if (method instanceof MyProj.MethodB)
return "methodB";
else if (method instanceof MyProj.MethodC)
return "methodC";
}
The markup where I bind the template looks like:
<div data-bind="template: {name: getTemplate($data), data: $data.Method}"></div>
This actually works very nice and when I change the type of method via an dropdown in the UI the domain model updates and the right template is shown. However here comes my problem. Each template contains a number of different fields that are specific for each method type. Whenever I change one of the values in the view model displayed by one of the templates the UI flashes and I think that happens because the template get selected again. This is quite irritating and looks extremly bad.
Any ideas on how to solve this problem? Any help would be greatly appreciated!
Thanks in advance
/Björn
Did you use any observable inside the getTemplate function. Updating the value of that observable makes the template rerender and you get your flash effect.
Checkout this link Part : "Note 5: Dynamically choosing which template is used".