I'm working on a Shiny module and generating data table using Javascript code through Ajax. But when the module is used in a Shiny app, Chrome keeps saying there is synchronous XMLHttpRequest on the main thread and I can't figure out why.
The module ui is generated by Javascript code which has a data table listing some data objects retrieved by Ajax call to a local desktop application's RESTful API. There is two buttons in the UI, one is 'Refresh', when it is clicked, ajax.reload() gets called while the other one is an 'Import' button which will import the selected row into the Shiny app.
I'm assuming the issue is not caused by the 'Import' button because when the page loads, the warning message shows up immediately, the 'Import' button didn't get clicked and plus the importing process is explicitly specified to use an async call. So the issue is caused by the initial ajax call that initializes the data table but I think it should default use a async call as well.
Any suggestions? Thanks!
wrapWithDocumentReady <- function(jsCode) {
return(paste0("$(document).ready(function(){", jsCode, "});"))
}
myModuleUI <- function(id) {
ns <- NS(id)
jsCode <- paste0(
# 1st to initialize the data table
"let objectTable = $('#",
ns("dataObjectTable"),
"').DataTable( {
ajax: {
url: 'http://localhost:XXXX/some_application/data',
dataSrc: ''
},
columns: [
{ name: 'name', data: 'name', title: 'Name' },
{ name: 'data_type', data: 'data_type', title: 'Data Type', visible:false },
{ name: 'id', data: 'id', title: 'ID', visible: false },
{ name: 'dimension', data: 'dimension', title: 'Dimension'},
],
select: {
style: 'single'
},
} );",
# 2nd to bind the 'Import' button with event
"$('#",
ns("importButton"),
"').click(function() {",
"
let selectedDataId = objectTable.cell('.selected', 'id:name').data();
var xmlhttp = new XMLHttpRequest();
var url = 'http://localhost:XXXX/some_application/data/'.concat(selectedDataId);
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
var dataJson = this.responseText;
Shiny.onInputChange('", ns("addDataObject"),"', dataJson);
}
}
};
xmlhttp.open('GET', url, true);
xmlhttp.send();
});",
# 3rd to bind the 'Reresh' button with event
"$('#",
ns("refreshButton"),
"').click(function() {
objectTable.ajax.reload();
});"
);
tagList(
div(tags$table(id = ns("dataObjectTable"), width="100%")),
actionButton(ns("refreshButton"), "Refresh"),
actionButton(ns("importButton"), "Import"),
tags$script(wrapWithDocumentReady(jsCode))
)
}
myModule <- function(input, output, session) {
importedData <- reactive ({
jsonStr <- input$addDataObject
if (!is.null(jsonStr)) {
# passing the data and return
} else {NULL}
})
return(importedData)
}
Related
I am working on extjs 3.4.0 and I wanted to add extra parameter in request to identify whether respective button is clicked or not (lets say clear filter button).
I have added that parameter in following way-
tbar: new Ext.PagingToolbar({
pageSize: 25,
store: PHOPHTStore,
displayInfo: true,
displayMsg: 'Displaying reports {0} - {1} of {2}',
emptyMsg: "No reports to display",
plugins: [PHOPHTFilters],
items:['-',{
text: 'Clear Filters',
iconCls:'x-btn-text-icon',
icon:'../images/tmp/cancel.gif',
tooltip:'Clear currently applied filters',
handler: function() {
PHOPHTGrid.filters.clearFilters();
PHOPHTStore.load({ params: { actionFilter: "clear" } });
}
}
})
Now the situation is I have added this { actionFilter: "clear" } when clear filter button is clicked.But this parameter is carried forward in every next request.I want to remove this as soon as this request is occurred OR when next request is demanded like ascending/descending column OR any other request.
I was planning to to this in -
listeners: {
'beforeload' : function() {
loadMask.msg = "Loading Reports(s), please wait...";
loadMask.show();
},
'load' : function() {
loadMask.hide();
}
}
Is there any other any way to store this parameter at this button click
OR
How can I remove this added parameter in any way?
please suggest
You can try Ext.Ajax.extraParams. I use this approach when loading data from server.
Partial example:
xloaddata: function() {
var me = this;
var v = me.edit_search.getValue();
me.store.proxy.extraParams = {
tablename: me.xtablename,
filter: v
)
};
me.store.loadPage(1);
me.store.proxy.extraParams = {
tablename: me.xtablename
};
}
I've just defined a combobox. Firstly it loads a countrylist and when select a value it's fire a change event which doing a ajax query to DB within searching service;
The thing; this configuration works pretty well when I click and open combobox items. But when I'm typing to combobox's field it's fires listener's store.load and because of none of country selected yet, the search query url gives not found errors of course.
{
xtype: 'countrycombo',
itemId: 'countryName',
name:'country',
afterLabelTextTpl: MyApp.Globals.required,
allowBlank: false,
flex: 1,
// forceSelection: false,
// typeAhead: true,
// typeAheadDelay: 50,
store: {
proxy: {
type: 'ajax',
// isSynchronous: true,
url: MyApp.Globals.getUrl() + '/country/list?limit=250',
// timeout: 300000,
reader: {
type: 'json',
rootProperty: 'data'
}
},
pageSize: 0,
sorters: 'description',
autoLoad: true
}
,
listeners: {
change: function (combo, countryId) {
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy()
.setUrl(MyAppp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
// Ext.defer(cityStore.load, 100);
cityStore.load();
}
}
},
I've tried several things as you see in code above to set a delay/timeout for load during typing to combobox text field; Ext.defer, timeoutconfig on proxy, typeAhead config on combo but none of them worked!
I thought that Ext.defer is the best solution but it gives this error:
Uncaught TypeError: me.getAsynchronousLoad is not a function at load (ProxyStore.js?_dc=15169)
How can I set a delay/timeout to combobox to fires load function?
Instead of Ext.defer(cityStore.load, 100);
try using this :
Ext.defer(function(){
cityStore.load
}, 300);
If this doest work, try increasing your delay
or you can put a logic before loading
like this :
if(countryId.length == 5){
cityStore.load
}
This will ensure that you Entered the right values before loading
Hope this helps, and Goodluck on your project
well.. I've tried to implement #Leroy's advice but somehow Ext.defer did not fire cityStore.load. So I keep examine similar situations on google and found Ext.util.DelayedTask
So configured the listerens's change to this and it's works pretty well;
listeners: {
change: function (combo, countryId) {
var alert = new Ext.util.DelayedTask(function () {
Ext.Msg.alert('Info!', 'Please select a country');
});
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy().setUrl(MyApp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
if (typeof countryId === 'number') {
cityStore.load();
} else {
alert.delay(8000);
}
}
}
The Problem
So i am currently trying to implement a color picker inside of a Kendo grid, that will hopefully send the chosen color to my Sql Table. Unfortunately, It doesn't seem as though the Update controller is being reached. I am relatively new to Kendo UI, so there might be some incredibly dumb errors shown.
Questions
I guess my main question would be: How can i call the update method when update is clicked on the grid. Essentially, the color picker and the edit command are showing up in beautiful fashion. I just want to know how i can be sure that the method is being called when 'Update' is clicked, seeing as it is not reaching my controller. Feel free to ask if you need to see more code or perhaps a screen shot.
Code
Config.cshtml ( Grid )
#model IEnumerable<STZN.Models.AGCData.ErrorCode>
#{
ViewBag.Title = "Config";
}
#section HeadContent{
<script src="~/Scripts/common.js"></script>
<script>
$(document).ready(function () {
$("#grid").kendoGrid({
editable: "inline",
selectable: "row",
dataSource: {
schema: {
model: {
id: "error_code",
fields: {
color: { type: 'string' }
}
}
},
transport: {
read: {
type: "POST",
dataType: "json",
url: "#Url.Action("ErrorCodes")"
},
update: {
type: "POST" ,
dataType: "json",
url: "#Url.Action("UpdateErrorCodes")",
}
}
},
columns: [
{ command : [ "edit" ] },
{
field: "error_code", title: "Error Code",
},
{
field: "error_description", title: "Error Description"
},
{
field: "color",
width: 150,
title: "Color",
template: function (dataItem) {
return "<div style = 'background-color: " + dataItem.color + ";' </div>"
},
editor: function (container, options) {
var input = $("<input/>");
input.attr("color",options.field);
input.appendTo(container);
input.kendoColorPicker({
value: options.model.color,
buttons: false
})
},
}
]
});
});
</script>
}
Update Controller
public JsonResult UpdateErrorCodes(ErrorCode model)
{
using (var db = new AgcDBEntities())
{
db.Entry(model).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
db.Configuration.ProxyCreationEnabled = false;
var data = db.ErrorCodes.Where(d => d.error_code == model.error_code).Select(x => new
{
error_code = x.error_code,
description = x.error_description,
color = x.color,
});
return new JsonResult()
{
JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet,
};
}
}
I actually managed to fix my issue by adding an additional input attribute to my editor function in the "color" field. It looks like this:
input.attr("data-bind","value:" + options.field);
There are still some present issues (unrelated to the fix/server update) , but as far as updating to the server, It work's as intended.
I have a web application with multiple Selectize objects initialized on the page. I'm trying to have each instance load a default value based on the query string when the page loads, where ?<obj.name>=<KeywordID>. All URL parameters have already been serialized are are a dictionary call that.urlParams.
I know there are other ways to initializing Selectize with a default value I could try; but, I'm curious why calling setValue inside onInitialize isn't working for me because I'm getting any error messages when I run this code.
I'm bundling all this JavaScript with Browserify, but I don't think that's contributing to this problem.
In terms of debugging, I've tried logging this to the console inside onInititalize and found that setValue is up one level in the Function.prototype property, the options property is full of data from load, the key for those objects inside options corresponds to the KeywordID. But when I log getValue(val) to the console, I get an empty string. Is there a way to make this work or am I ignoring something about Selectize or JavaScript?
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
onInitialize: function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
this.setValue(val);
},
load: function(query, callback) {
$.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
})
}
});
};
...
After sprinkling in some console.logs into Selectize.js, I found that the ajax data hadn't been imported, when the initialize event was triggered. I ended up finding a solution using jQuery.when() to make setValue fire after the data had been loaded, but I still wish I could find a one-function-does-one-thing solution.
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
load: function(query, callback) {
var self = this;
$.when( $.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
}) ).then(function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
self.setValue(val);
});
}
});
};
...
You just need to add the option before setting it as the value, as this line in addItem will be checking for it:
if (!self.options.hasOwnProperty(value)) return;
inside onInitialize you would do:
var val = that.urlParams[that.name]; //It might work with parseInt, I haven't used integers in selectize options though, only strings.
var opt = {id:val, text:val};
self.addOption(opt);
self.setValue(opt.id);
Instead of using onInitialize you could add a load trigger to the selectize. This will fire after the load has finished and will execute setValue() as expected.
var $select = $(this).container.selectize({
// ...
load: function(query, callback) {
// ...
}
});
var selectize = $select[0].selectize;
selectize.on('load', function(options) {
// ...
selectize.setValue(val);
});
Note that for this you first have to get the selectize instanze ($select[0].selectize).
in my case it need refresh i just added another command beside it
$select[0].selectize.setValue(opt);
i added this
$select[0].selectize.options[opt].selected = true;
and changes applied
but i dont know why?
You can initialize each selectize' selected value by setting the items property. Fetch the value from your querystring then add it as an item of the items property value:
const selectedValue = getQueryStringValue('name') //set your query string value here
$('#sel').selectize({
valueField: 'id',
labelField: 'title',
preload: true,
options: [
{ id: 0, title: 'Item 1' },
{ id: 1, title: 'Item 2' },
],
items: [ selectedValue ],
});
Since it accepts array, you can set multiple selected items
Sometimes I like to use the HTML5/Javascript implementations of the Kendo framework because you can do some things a little easier. In this case I need to know the number of results so I can either display a kendo grid or not, however other times I need to modify the datasource based on user input on the client side. Unfortunately you can't get the number of results or modify the datasource (as far as I know) using the MVC wrappers. How can I call the controller using the Javascript implementation of the Kendo datasource?
I was able to get this working using the following code:
Controller:
public ActionResult GetStuff(string parameter)
{
// Get your data here ...
var data = GetData(parameter);
return Json(data, JsonRequestBehavior.AllowGet);
} // end
Markup/cshtml:
<div id='myGrid'></div>
<script>
$(document).ready(function () {
// Define the dataSource, note that the schema elements are specified
var dataSource = new kendo.data.DataSource({
dataType: "json",
type: "GET",
transport: {
read: '#Url.Action("MethodName", "ControllerName", new {parameter = myParameter} )'
},
schema: {
data: "Stuff",
total: "TotalNumberofStuff",
errors: "ErrorMessage"
}
});
}
// Call fetch on the dataSource - this gets the data - the fetch method will make only one call.
// Please note that the datasource fetch call is async, so we must use it's results within the fetch function.
dataSource.fetch(function () {
var numberOfItems = dataSource.total();
if (numberOfItems == 0) {
// If 0 items are returned show the label that says there are no items
$("#myGrid").append("<p><label style='font-size: small; color: red;'>-- No Items --</label></p>");
}
else {
$("#myGrid").kendoGrid({
dataSource: dataSource,
height: function () {
return (numberOfItems >= 1 && numberOfItems <= 5) ? null : "225";
},
columns: [
{ field: "StuffId", title: "Id", width: 150 },
{ field: "Stuff", title: "Stuff", width: 150 }
]
});
}
});
</script>