We're working with knockout.js and knockout.mapping.js on .NET MVC 4. Let's say I have such JSON:
{
"deliveryPointType": "0",
"deliveryPointTypes": [
{
"id": 0,
"text": "Pridėti rankiniu būdu"
},
{
"id": 1,
"text": "Siųsti visiems regiono objektams"
}
],
"showRegionSelection": false,
"showDeliveryPointSelection": true,
"regionId": "",
"userHasRegions": "False",
"propertyNames": {
"deliveryPointTypeName": "Pridėti rankiniu būdu"
},
"initialMaterials": [
{
"quantity": 0,
"materialTypeId": "",
"propertyNames": {},
"validMaterial": true,
"showMaterialError": false,
"materialTypeAjax": {
"quietMillis": 300,
"cache": false,
"dataType": "json",
"type": "GET",
"url": "/lt-LT/Material/MaterialTypeNameLookup"
}
}
],
"deliveryBuildings": [
{
"clientId": "1",
"buildingId": "1",
"regionId": "",
"newBuilding": false,
"validClient": true,
"validBuilding": true,
"validRegion": true,
"showClientError": false,
"showBuildingError": false,
"showRegionError": false,
"propertyNames": {
"clientName": "klientas",
"buildingName": "ASD project, Antagynės gatvė, Kaunas, Lietuvos Respublika"
},
"clientAjax": {
"quietMillis": 300,
"cache": false,
"dataType": "json",
"type": "GET",
"url": "/lt-LT/Task/PayerLookup"
},
"buildingAjax": {
"quietMillis": 300,
"cache": false,
"dataType": "json",
"type": "GET",
"url": "/lt-LT/Object/GetClientAddressListByQuery"
},
"regionAjax": {
"quietMillis": 300,
"cache": false,
"dataType": "json",
"type": "GET",
"url": "/lt-LT/Object/RegionNameLookup"
}
}
],
"hasNewBuildings": false,
"showBuildingValidation": false,
"showMinimumBuildingRequiredValidation": false,
"showMaterialValidation": false,
"validRegion": true,
"showRegionError": false,
"regionAjax": {
"quietMillis": 300,
"cache": false,
"dataType": "json",
"type": "GET",
"url": "/lt-LT/Object/RegionNameLookup"
}
}
On form submit fail (if something goes wrong/invalid in service) it is repopulated with previous values. We convert ViewModel to JSON on form submit with $('#BuildingsJSON').val(ko.mapping.toJSON(viewModel.deliveryBuildings)).
On form repopulating, we parse JSON with ko.mapping.fromJSON(deliveryBuildings, mapping, viewModel.deliveryBuildings)(); mapping is just an empty object for now (tried "ignore" with no luck).
We use select2 fields to select addresses of buildings from a list (using ajax). Thing is, fromJSON populates almost every json property as observable, which I don't need. On select2 open we get an exception:
Uncaught TypeError: Object function observable() {
if (arguments.length > 0) {
// Write
// Ignore writes if the value hasn't changed
if (!observable['equalityComparer'] || !observable['equalityComparer'](_latestValue, arguments[0])) {
observable.valueWillMutate();
_latestValue = arguments[0];
if (DEBUG) observable._latestValue = _latestValue;
observable.valueHasMutated();
}
return this; // Permits chained assignments
}
else {
// Read
ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read"
operation
return _latestValue;
}
} has no method 'toUpperCase'
I've debugged where it breaks - on type property of ajax call. I figured that we need to exclude ajax properties from casting to observable.
So, the question is: how can one not cast specific properties of specific objects to observable()? Is it enough using mapping plugin, is additional plugin needed or is it even impossible?
Have you looked at using the copy keyword in your mapping binding? This just copies the value over into a js property rather than making it an observable.
From the documentation:
var mapping = {
'copy': ["propertyToCopy"]
}
ko.mapping.fromJS(data, mapping, viewModel);
Related
I'm calling a JSON file (sections.json), and then accessing the array sections, looping through every item and appending the name value to a li. I wish to give this li a link (or click event) to display the articles names of the corresponding section on .sectionArticles. To access the articles of a specific section, I have to access a different JSON file (sections/{section id}/articles.json). I'm clueless on this last part... could someone point me out in the right direction?
Here is my current code so far:
$(function() {
// URLs
var zendeskUrl = 'https://myhelpcenter.zendesk.com/'
var sectionsUrl = zendeskUrl+'api/v2/help_center/pt-br/sections.json';
var articlesUrl = zendeskUrl+'api/v2/help_center/pt-br/articles.json?per_page=100';
$.ajax({
type: 'GET',
url: sectionsUrl,
success: function(data) {
data.sections.forEach(function(section) {
$('.sectionsList').append('<li class="sectionName">' + section.name + '</li>');
})
},
error: function() {
console.log("Error: sectionsUrl");
}
})
$.ajax({
type: 'GET',
url: articlesUrl,
success: function(data) {
data.articles.forEach(function(article) {
if (article.promoted == true) {
$('.promotedList').append('<li class="promotedName">' + article.name + '</li>');
}
})
},
error: function() {
console.log("Error: articlesUrl");
}
})
})
JSOM Sample:
List ALL Sections: /api/v2/help_center/sections.json
{
"sections": [
{
"id": 115001417087,
"url": "/api/v2/help_center/sections/115001417087.json",
"html_url": "/hc/sections/115001417087",
"category_id": 115000835587,
"position": 0,
"sorting": "manual",
"created_at": "2017-03-22T14:29:48Z",
"updated_at": "2017-06-13T20:01:01Z",
"name": "Section 1",
"description": "",
"locale": "",
"source_locale": "",
"outdated": false
}
],
"page": 1,
"previous_page": null,
"next_page": null,
"per_page": 30,
"page_count": 1,
"count": 8,
"sort_by": "position",
"sort_order": "asc"
}
List ALL Articles from Section {id}: /api/v2/help_center/sections/{id}/articles.json */
{
"count": 9,
"next_page": null,
"page": 1,
"page_count": 1,
"per_page": 30,
"previous_page": null,
"articles": [
{
"id": 115008623727,
"url": "/api/v2/help_center/articles/115008623727.json",
"html_url": "/hc/articles/115008623727",
"author_id": 20423232608,
"comments_disabled": true,
"draft": false,
"promoted": false,
"position": 0,
"vote_sum": 0,
"vote_count": 0,
"section_id": 115001417087,
"created_at": "2017-06-13T19:46:17Z",
"updated_at": "2017-06-13T19:59:28Z",
"name": "Some name",
"title": "Some title",
"body": "Some HTML",
"source_locale": "",
"locale": "",
"outdated": false,
"outdated_locales": [],
"label_names": []
}
],
"sort_by": "position",
"sort_order": "asc"
}
I don't know if I understood the question the right way, but I assume you load the section list in the beginning and then want to load articles only when requested.
Therefore I'd first store the section id in the section list, so I can reuse it later on.
$.ajax({
type: 'GET',
url: sectionsUrl,
success: function(data) {
data.sections.forEach(function(section) {
$('.sectionsList').append('<li class="sectionName" data-section-id="' + section.id + '">' + section.name + '</li>');
})
},
error: function() {
console.log("Error: sectionsUrl");
}
})
With .on('click'), you already went into the right direction but since the sections are generated after event binding took place, you need event delegation to react on your click.
you can read more on this here: https://learn.jquery.com/events/event-delegation/
Additionally, I added the empty() call to clear the article list. If there are previous results, you can move that line into the ajax success function. If you want to keep the old list as long as no valid response has been returned, in terms of usability, I wouldn't. Keeping it doesn't show the user that something is happening. They might wait a moment and click again and again and again and so on. Better rework the error function to show something in the list instead of the console.log.
$(".sectionsList").on("click", ".sectionName", function(){
clickedSectionId = $(this).data("sectionId");
$('.promotedList').empty(); //clear list of previous results
articlesUrl = zendeskUrl + 'api/v2/help_center/' + clickedSectionId + '/articles.json?per_page=100';
$.ajax({
type: 'GET',
url: articlesUrl,
success: function(data) {
data.articles.forEach(function(article) {
if (article.promoted == true) {
$('.promotedList').append('<li class="promotedName">' + article.name + '</li>');
}
})
},
error: function() {
console.log("Error: articlesUrl");
}
});
});
I assume that your ajax calls are setup the way you need them. Just focus on storing the section id and binding the click function the correct way.
I am sending an uploaded file from my HTML form to my server and am trying to add a custom attribute to FormData(), but it's not showing up on my server side.
I add my custom attribute by doing var formData = new FormData();, then appending by doing formData.append("airlinename",airline_name);, but once I get the data on the Server side, I look in the req object and can't find airlinename. How do I access my custom attribute?
I am able to access the file just fine, but I can't find how to access the custom attribute I appended to formData.
HTML Form
<form role="form">
<input type="text" id="load_db_name" name="load_db_name">
<input type="file" id="load_db_dir" name="load_db_dir">
</form>
<button id="load_generateDiagram" onClick="loadPastDiagram();" type="button">Load</button>
Client JS
function loadPastDiagram()
{
var db_dir = document.getElementById('load_db_dir').files[0] || null;
var _files = [db_dir];
var airline_name = document.getElementById('load_db_name').value.trim();
loadDiagram(airline_name,_files);
}
function loadDiagram(airline_name, files)
{
var formData = new FormData();
for (var f in files) {
formData.append("files", files[f]);
}
formData.append("airlinename",airline_name); //<--- can't find this on the server side
$.ajax({
url: '/loadDiagram',
type: 'POST',
success: function(res) {
console.log("Success");
},
error: function(err) {
console.log("Error ",err);
},
data: formData,
//Options to tell jQuery not to process data or worry about content-type.
cache: false,
contentType: false,
processData: false
});
}
Server JS
app.post('/loadDiagram', function(req,res){
console.log("[FILES]" + JSON.stringify(req.airlinename));
console.log("[FILES]" + JSON.stringify(req.files.airlinename));
console.log("[FILES]" + JSON.stringify(req.files.files));
});
Output
[DEV] [FILES]undefined
[DEV] [FILES]undefined
[DEV] [FILES]{
"fieldName": "files",
"originalFilename": "Tool_fresshhh.tar.gz",
"path": "../Output-Files/2833-fwh0ql.tf9od2t9.gz",
"headers": {
"content-disposition": "form-data; name=\"files\"; filename=\"Tool_fresshhh.tar.gz\"",
"content-type": "application/x-gzip"
},
"ws": {
"_writableState": {
"objectMode": false,
"highWaterMark": 16384,
"needDrain": true,
"ending": true,
"ended": true,
"finished": true,
"decodeStrings": true,
"defaultEncoding": "utf8",
"length": 0,
"writing": false,
"corked": 0,
"sync": false,
"bufferProcessing": false,
"writecb": null,
"writelen": 0,
"bufferedRequest": null,
"lastBufferedRequest": null,
"pendingcb": 0,
"prefinished": true,
"errorEmitted": false,
"bufferedRequestCount": 0,
"corkedRequestsFree": {
"next": {
"next": null,
"entry": null
},
"entry": null
}
},
"writable": false,
"domain": null,
"_events": {
"error": [null],
"close": [null]
},
"_eventsCount": 2,
"path": "../Output-Files/2833-fwh0ql.tf9od2t9.gz",
"fd": null,
"flags": "w",
"mode": 438,
"autoClose": true,
"bytesWritten": 449781,
"closed": true
},
"size": 449781,
"name": "Tool_fresshhh.tar.gz",
"type": "application/x-gzip"
}
Versions
jQuery v1.11.1
Express Version: 3.14.0
Node Version: v6.5.0
I know this is "uncool"...
But I saw this question as it was posted, more than 1 hour ago, and saw (quite fast!!) the solution posted as a comment.
I waited on him or her to post it, so I could learn something.
But since Jaromanda X was actually last seen 5 minutes ago without posting the answer...
I guess he or she is not really interested about rep. points!
So just to mark this question as answered...
;)
Server JS
app.post('/loadDiagram', function(req,res){
console.log("[FILES]" + JSON.stringify(req.body.airlinename)); // <--
console.log("[FILES]" + JSON.stringify(req.files.files));
});
«First one to post it, gets it! »
I'm trying to create a combobox with pagination (virtualization) using Kendo UI and Angular JS:
The idea is to bring 10000 items in pages of 10. Using this and this Kendo Demos I get this far:
HTML:
<select kendo-combo-box k-options="config"></select>
Angular Controller:
$scope.config = {
template: "<span>#= key # - #= value #</span>",
dataTextField: "key",
dataValueField: "value",
virtual: {
itemHeight: 26,
valueMapper: function (options) {
console.log("valueMapper was executed.");
},
},
height: 156,
dataSource: {
//type: "odata", // comment explained below
pageSize: 24,
serverPaging: true,
//serverFiltering: true,
schema: {
total: "total",
data: "data"
},
transport: {
read: function (options) {
console.log("dataSource transport read was called.");
// ajax call with result object => response.
options.success(response);
}
},
}
};
JSON response object (in transport read function):
{
"total": "10000",
"data": [
{ "key": "1", "value": "a" },
{ "key": "2", "value": "b" },
...
{ "key": "9", "value": "j" }
]
}
PS: "data" has 10 elements.
This is not working. The combo is created with 10 items and nothing more.
If I uncomment the lines:
type: "odata",
serverFiltering: true,
The combobox is build with 10000 items with "loading..." as description. But no matter how much time I wait, this "loading..." texts are never updated to my information, and more, the line:
console.log("dataSource transport read was called.");
keep getting hit in infinity loop.
Before finishes I notice other strange behavior. The function in valueMapper is never executed (the message "valueMapper was executed." is never printed.).
And if I set virtual: false no pagination is used.
How can I make this work?
I have an issue with the reinitialisation of my datatable. My code below works by pulling in a json from getOrderStatus.php and upon success of this puts all the json data into javascript variables and then from this i can set these variables to div tags and display the data i need on my webpage. However the Datatable cannot be reinititalised once the ajax loop runs and displays the following error message "DataTables warning: table id=mytable - Cannot reinitialise DataTable". I believe i need a way to kill the table and recreate it upon the ajax refresh however i cant seem to find a way to do this ?
$(document).ready(function ajaxLoop(){
$.ajax({
url: 'getOrderStatus.php', // Url of Php file to run sql
data: "",
dataType: 'json', //data format
success: function updateOrder(data) //on reciept of reply
{
var OrdersSubmitted = data.OrderStatus[0].SUBMITTED; //get Orders Submitted Count
var OrdersFulfilled = data.OrderStatus[0].FULFILLED; //get Orders Fulfilled count
var LastTransaction = data.LastTransaction[0]; //get Last Transaction
//--------------------------------------------------------------------
// 3) Update html content
//--------------------------------------------------------------------
$('#OrdersSubmitted').html(OrdersSubmitted);
$('#OrdersFulfilled').html(OrdersFulfilled); //Set output html divs
$('#mytable').dataTable({
"data": LastTransaction,
"aging": false,
"searching": false,
"columns": [
{ "title": "ORDER_ID" }, // <-- which values to use inside object
{ "title": "STATUS" },
{ "title": "ACC_NUMBER" },
{ "title": "SORT_CODE" }
]
});
setTimeout(ajaxLoop, 2000);
}
});
});
Did you try using "bDestroy": true.
$('#mytable').dataTable({
"data": LastTransaction,
"aging": false,
"searching": false,
"bDestroy": true,
"columns": [
{ "title": "ORDER_ID" }, // <-- which values to use inside object
{ "title": "STATUS" },
{ "title": "ACC_NUMBER" },
{ "title": "SORT_CODE" }
]
});
Another way is if you check if datatable is already init. on your table
var table = $('#mytable');
if ($.fn.DataTable.fnIsDataTable(table)) {
//It's already a datatable
//clear and destroy
table.dataTable().fnClearTable();
table.dataTable().fnDestroy();
}
**It seems your are using latest datatable version:**
then option should be destroy:true (aging should be changed to paging):
$('#mytable').dataTable({
"data": LastTransaction,
"paging": false,
"searching": false,
"destroy": true,
"columns": [
{ "title": "ORDER_ID" }, // <-- which values to use inside object
{ "title": "STATUS" },
{ "title": "ACC_NUMBER" },
{ "title": "SORT_CODE" }
]
});
and check on existing datatable would be:
if($.fn.DataTable.isDataTable("#myTable"))
{
$('#myTable').DataTable().clear().destroy();
}
I am trying to quickly create a simple display of results from an internal API using DataTables. The API returns JSON in the following structure:
obj {
status: 1,
results: 100,
offset: 25,
limit: 25,
data: [
[1]: {
title: "Blah blah one",
description: "Doesn't really matter",
misc: "Yadda yadda"
},
[2]: {
title: "Blah blah two",
description: "Doesn't really matter",
misc: "Yadda yadda"
},
]
}
I can't/don't want to change the API structure just because DataTables uses a weird structure, but I would like to access the built in functionalities for paging, dynamic loading, etc. DataTables seems to allow for custom data objects, and I've gotten the table to load with the following:
$(document).ready(function() {
$('#example').dataTable( {
"bProcessing": true,
"bServerSide": true,
"sAjaxSource": "http://api.oursite.com/api?limit=100",
"fnServerData": function( sUrl, aoData, fnCallback, oSettings ) {
oSettings.jqXHR = $.ajax( {
"url": sUrl,
"data": aoData,
"success": fnCallback,
"dataType": "jsonp",
"cache": false
} );
},
"sAjaxDataProp": "data",
"aoColumns": [
{ "mData": "title" },
{ "mData": "description" },
{ "mData": "misc" },
]
} );
});
But, none of the paging or sorting functions work. I think this is because DataTables requires a results count and paging variable in the object— "iTotalRecords" and "iTotalDisplayRecords". Is this correct? Is there any way to use the api variables instead? Thanks in advance. I'm currently not getting any errors in the dev console, so if it's erroring it's doing so silently...
While initializing the datatable, instead of directly assigning source to Ajaxsource, you can set the aaData to the javascript function where you can manipulate to return only obj.data. You need handle few things manually.
$('#tblExample').dataTable({
"bJqueryUI": true,
"bDestroy":true,
"bSortable": false,
"sAjaxSource": "",
"aaData":GetData(),
"aoColumns": [
{
"sTitle":"Index","mDataProp": null, "sWidth": "20px", "sDefaultContent": "<span class='ui-icon ui-icon-circle-close' onclick='RemoveActiveItem(this);'></span>", "bSortable": false},
{ "mDataProp": "Year"},
{ "mDataProp": "Month"},
{ "mDataProp": "Savings"}
]
});