TypeError: callback is not a function. (In 'callback(data)',
'callback' is an instance of Object)
The code here works just fine when I write it like this:
const onSelectFilterDone = (filter) => {
setFilter(filter);
setFilterModalVisible(false);
unsubscribe.current = listingsAPI.subscribeListings(
{ categoryId: category.id },
// { categoryId2: category2.id },
favorites,
onListingsUpdate,
);
};
When i uncomment that other line, it breaks and gives me this error.
const onSelectFilterDone = (filter) => {
setFilter(filter);
setFilterModalVisible(false);
unsubscribe.current = listingsAPI.subscribeListings(
{ categoryId: category.id },
{ categoryId2: category2.id },
favorites,
onListingsUpdate,
);
};
Here is the relevant snippet from listingsAPI (below) if it helps but this code works fine when there is only one object. Is there a specific way to make this work with two objects like above?
if (categoryId) {
return (
listingsRef
.where('categoryID', '==', categoryId)
.where('isApproved', '==', isApproved)
.onSnapshot((querySnapshot) => {
const data = [];
querySnapshot.forEach((doc) => {
const listing = doc.data();
if (favorites && favorites[doc.id] === true) {
listing.saved = true;
}
data.push({ ...listing, id: doc.id });
});
callback(data);
})
);
}
if (categoryId2) {
return (
listingsRef
.where('categoryID2', '==', categoryId2)
.where('isApproved', '==', isApproved)
.onSnapshot((querySnapshot) => {
const data = [];
querySnapshot.forEach((doc) => {
const listing = doc.data();
if (favorites && favorites[doc.id] === true) {
listing.saved = true;
}
data.push({ ...listing, id: doc.id });
});
callback(data);
})
);
}
You can combine your queries via this way if you want to have it optional:
let query = listingsRef.where('isApproved', '==', isApproved)
if (categoryId) {
query = query.where('categoryID', '==', categoryId)
}
if (categoryId2) {
query = query.where('categoryID2', '==', categoryId2)
}
query.onSnapshot...
I am trying to build Search function in SectionList. I have search inside the 'data' (second field) and not inside 'title' but I am not able to make it work.
My Data is about the Flat / resident details of an Apartment -
sectiondata =
[{"title":"GROUND FLOOR",
"data":[
{"id":"48","res_type":"owner","user_name":"Ashwani","flat_id":"1","flat_name":"001","floor_no":"GROUND FLOOR","floor_int":"0","signal_player_id":"aa","user_phone":"98855550"},
{"id":"49","res_type":"owner","user_name":"Rahul","flat_id":"2","flat_name":"002","floor_no":"GROUND FLOOR","floor_int":"0","signal_player_id":"aa","user_phone":"999999"}
]
}]
I am trying something like this but it is not working.
searchFilterFunction = (text) => {
let search = text.toLowerCase();
this.setState({
check: this.state.sectiondata.filter(
obj => obj.data['flat_name'].toLowerCase().includes(search))
});
}
How to filter data base on name? Please assist here.
Thanks.
You can try to search like this:
onChangeText(text) {
if (text.trim().length > 0) {
var temp = []
sectiondata.map((item) => {
var dataItem = {};
var brandData = [];
item.data.map((searchItem) => {
let flatName = searchItem.flat_name
if (flatName.match(text)) {
brandData.push(searchItem);
}
})
if (brandData.length > 0) {
} else {
return null;
}
dataItem.brandData = brandData;
temp.push(dataItem);
this.setState({
sectiondata: temp
})
})
} else {
this.setState({
sectiondata: this.state.tempData
})
}
}
searchFilterFunction(text) {
if( text == undefined || text == '') {
this.setState({
sectiondata: this.arrayholder
})
return;
}
if (text.trim().length > 0) {
var temp = []
this.state.sectiondata.map((item) => {
var dataItem = {};
var title = item.title;
var brandData = [];
item.data.map((searchItem) => {
let flatName = searchItem.flat_name
if (flatName.match(text)) {
brandData.push(searchItem);
}
})
if (brandData.length > 0) {
} else {
return null;
}
dataItem.title = title;
dataItem.data = brandData;
temp.push(dataItem);
this.setState({
sectiondata: temp
})
})
I'd like to send base64 image as an attachment to a trello card through the API
POST /1/cards/[card id or shortlink]/attachments
There's a file field but it does not specify how should look the data there.
Refs: https://developers.trello.com/advanced-reference/card#post-1-cards-card-id-or-shortlink-attachments
Any idea?
Short answer: Trello's API only works to attach binary data via multipart/form-data. Examples below.
Long answer:
The Trello API to add attachments and images is frustratingly under-documented. They do have a coffeescript Client.js for those of us using Javascript to simplify all the basic operations: https://trello.com/1/client.coffee
Using the vanilla Client.js file I have been able to attach links and text files. While the CURL example shows pulling a binary file in from a hard drive, that doesn't work for those of us completely on a server or client where we don't have permissions to create a file.
From a LOT of trial and error, I've determined all binary data (images, documents, etc.) must be attached using multipart/form-data. Here is a jQuery snippet that will take a URL to an item, get it into memory, and then send it to Trello.
var opts = {'key': 'YOUR TRELLO KEY', 'token': 'YOUR TRELLO TOKEN'};
var xhr = new XMLHttpRequest();
xhr.open('get', params); // params is a URL to a file to grab
xhr.responseType = 'blob'; // Use blob to get the file's mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData(); // this is the formdata Trello needs
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
I have submitted a new coffeescript for Trello developer support to consider uploading to replace Client.js. It adds a "Trello.upload(url)" that does this work.
I've also attached here for convenience in JS form.
// Generated by CoffeeScript 1.12.4
(function() {
var opts={"version":1,"apiEndpoint":"https://api.trello.com","authEndpoint":"https://trello.com"};
var deferred, isFunction, isReady, ready, waitUntil, wrapper,
slice = [].slice;
wrapper = function(window, jQuery, opts) {
var $, Trello, apiEndpoint, authEndpoint, authorizeURL, baseURL, collection, fn, fn1, i, intentEndpoint, j, key, len, len1, localStorage, location, parseRestArgs, readStorage, ref, ref1, storagePrefix, token, type, version, writeStorage;
$ = jQuery;
key = opts.key, token = opts.token, apiEndpoint = opts.apiEndpoint, authEndpoint = opts.authEndpoint, intentEndpoint = opts.intentEndpoint, version = opts.version;
baseURL = apiEndpoint + "/" + version + "/";
location = window.location;
Trello = {
version: function() {
return version;
},
key: function() {
return key;
},
setKey: function(newKey) {
key = newKey;
},
token: function() {
return token;
},
setToken: function(newToken) {
token = newToken;
},
rest: function() {
var args, error, method, params, path, ref, success;
method = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = parseRestArgs(args), path = ref[0], params = ref[1], success = ref[2], error = ref[3];
opts = {
url: "" + baseURL + path,
type: method,
data: {},
dataType: "json",
success: success,
error: error
};
if (!$.support.cors) {
opts.dataType = "jsonp";
if (method !== "GET") {
opts.type = "GET";
$.extend(opts.data, {
_method: method
});
}
}
if (key) {
opts.data.key = key;
}
if (token) {
opts.data.token = token;
}
if (params != null) {
$.extend(opts.data, params);
}
if (method === 'UPLOAD' && typeof (params) === "string" && params.length > 5) {
var xhr = new XMLHttpRequest();
xhr.open('get', params);
xhr.responseType = 'blob'; // Use blob to get the mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData();
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
} else {
return $.ajax(opts);
}
},
authorized: function() {
return token != null;
},
deauthorize: function() {
token = null;
writeStorage("token", token);
},
authorize: function(userOpts) {
var k, persistToken, ref, regexToken, scope, v;
opts = $.extend(true, {
type: "redirect",
persist: true,
interactive: true,
scope: {
read: true,
write: false,
account: false
},
expiration: "30days"
}, userOpts);
regexToken = /[&#]?token=([0-9a-f]{64})/;
persistToken = function() {
if (opts.persist && (token != null)) {
return writeStorage("token", token);
}
};
if (opts.persist) {
if (token == null) {
token = readStorage("token");
}
}
if (token == null) {
token = (ref = regexToken.exec(location.hash)) != null ? ref[1] : void 0;
}
if (this.authorized()) {
persistToken();
location.hash = location.hash.replace(regexToken, "");
return typeof opts.success === "function" ? opts.success() : void 0;
}
if (!opts.interactive) {
return typeof opts.error === "function" ? opts.error() : void 0;
}
scope = ((function() {
var ref1, results;
ref1 = opts.scope;
results = [];
for (k in ref1) {
v = ref1[k];
if (v) {
results.push(k);
}
}
return results;
})()).join(",");
switch (opts.type) {
case "popup":
(function() {
var authWindow, height, left, origin, receiveMessage, ref1, top, width;
waitUntil("authorized", (function(_this) {
return function(isAuthorized) {
if (isAuthorized) {
persistToken();
return typeof opts.success === "function" ? opts.success() : void 0;
} else {
return typeof opts.error === "function" ? opts.error() : void 0;
}
};
})(this));
width = 420;
height = 470;
left = window.screenX + (window.innerWidth - width) / 2;
top = window.screenY + (window.innerHeight - height) / 2;
origin = (ref1 = /^[a-z]+:\/\/[^\/]*/.exec(location)) != null ? ref1[0] : void 0;
authWindow = window.open(authorizeURL({
return_url: origin,
callback_method: "postMessage",
scope: scope,
expiration: opts.expiration,
name: opts.name
}), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
receiveMessage = function(event) {
var ref2;
if (event.origin !== authEndpoint || event.source !== authWindow) {
return;
}
if ((ref2 = event.source) != null) {
ref2.close();
}
if ((event.data != null) && /[0-9a-f]{64}/.test(event.data)) {
token = event.data;
} else {
token = null;
}
if (typeof window.removeEventListener === "function") {
window.removeEventListener("message", receiveMessage, false);
}
isReady("authorized", Trello.authorized());
};
return typeof window.addEventListener === "function" ? window.addEventListener("message", receiveMessage, false) : void 0;
})();
break;
default:
window.location = authorizeURL({
redirect_uri: location.href,
callback_method: "fragment",
scope: scope,
expiration: opts.expiration,
name: opts.name
});
}
},
addCard: function(options, next) {
var baseArgs, getCard;
baseArgs = {
mode: 'popup',
source: key || window.location.host
};
getCard = function(callback) {
var height, left, returnUrl, top, width;
returnUrl = function(e) {
var data;
window.removeEventListener('message', returnUrl);
try {
data = JSON.parse(e.data);
if (data.success) {
return callback(null, data.card);
} else {
return callback(new Error(data.error));
}
} catch (error1) {}
};
if (typeof window.addEventListener === "function") {
window.addEventListener('message', returnUrl, false);
}
width = 500;
height = 600;
left = window.screenX + (window.outerWidth - width) / 2;
top = window.screenY + (window.outerHeight - height) / 2;
return window.open(intentEndpoint + "/add-card?" + $.param($.extend(baseArgs, options)), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
};
if (next != null) {
return getCard(next);
} else if (window.Promise) {
return new Promise(function(resolve, reject) {
return getCard(function(err, card) {
if (err) {
return reject(err);
} else {
return resolve(card);
}
});
});
} else {
return getCard(function() {});
}
}
};
ref = ["GET", "PUT", "POST", "DELETE", "UPLOAD"];
fn = function(type) {
return Trello[type.toLowerCase()] = function() {
return this.rest.apply(this, [type].concat(slice.call(arguments)));
};
};
for (i = 0, len = ref.length; i < len; i++) {
type = ref[i];
fn(type);
}
Trello.del = Trello["delete"];
ref1 = ["actions", "cards", "checklists", "boards", "lists", "members", "organizations", "lists"];
fn1 = function(collection) {
return Trello[collection] = {
get: function(id, params, success, error) {
return Trello.get(collection + "/" + id, params, success, error);
}
};
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
collection = ref1[j];
fn1(collection);
}
window.Trello = Trello;
authorizeURL = function(args) {
var baseArgs;
baseArgs = {
response_type: "token",
key: key
};
return authEndpoint + "/" + version + "/authorize?" + $.param($.extend(baseArgs, args));
};
parseRestArgs = function(arg) {
var error, params, path, success;
path = arg[0], params = arg[1], success = arg[2], error = arg[3];
if (isFunction(params)) {
error = success;
success = params;
params = {};
}
path = path.replace(/^\/*/, "");
return [path, params, success, error];
};
localStorage = window.localStorage;
if (localStorage != null) {
storagePrefix = "trello_";
readStorage = function(key) {
return localStorage[storagePrefix + key];
};
writeStorage = function(key, value) {
if (value === null) {
return delete localStorage[storagePrefix + key];
} else {
return localStorage[storagePrefix + key] = value;
}
};
} else {
readStorage = writeStorage = function() {};
}
};
deferred = {};
ready = {};
waitUntil = function(name, fx) {
if (ready[name] != null) {
return fx(ready[name]);
} else {
return (deferred[name] != null ? deferred[name] : deferred[name] = []).push(fx);
}
};
isReady = function(name, value) {
var fx, fxs, i, len;
ready[name] = value;
if (deferred[name]) {
fxs = deferred[name];
delete deferred[name];
for (i = 0, len = fxs.length; i < len; i++) {
fx = fxs[i];
fx(value);
}
}
};
isFunction = function(val) {
return typeof val === "function";
};
wrapper(window, jQuery, opts);
}).call(this);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
Data is not binding to Kendo dropdown list list is returning form my method
My dropdown
#(Html.Kendo().DropDownListFor(model => model.ParentAssetID)
.OptionLabel(" ")
.Name("ParentAssetID")
.DataTextField("AssetName")
.DataValueField("AssetId")
.SelectedIndex(0)
.Text(string.Empty)
.DataSource(source =>
{
source.Read(read =>
{
read.Url("../Asset/GetAllAssetsByCompanyId");
});
}))
my Action Result
public IEnumerable<AssetDetails> GetAllAssetsByCompanyId()
{
IList<AssetDetails> _assetSearchlist;
using (var client = new HttpClient())
{
AssetRepository assetrep = new AssetRepository();
Guid cp = new Guid(Session["CurrentCompanyId"].ToString());
_assetSearchlist = assetrep.GetAllAssetsByCompanyId(cp, "", "", "");
return _assetSearchlist;
}
}
public JsonResult GetOpportunityListByAccount(string Id)
{
Guid ID = new Guid(Id);
List<OpportunityViewModel> cpvm = new List<OpportunityViewModel>();
List<CrmOpportunity> crmOppList = new List<CrmOpportunity>();
cpvm = srv.OpportunitySet.Where(z => z.CustomerId.Id == ID).ToList();
foreach (var crm in cpvm )
{
CrmOpportunity crmOpp = new CrmOpportunity();
crmOpp.Id = crm.Id;
crmOpp.Name = crm.Name;
crmOppList.Add(crmOpp);
}
return Json(crmOppList, JsonRequestBehavior.AllowGet);
}
#(Html.Kendo().DropDownListFor(x => x.FromOpportunity)
.Name("OpportunityDDL")
.DataTextField("Name")
.DataValueField("Id")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetOpportunityListByAccount", "CrmIntegration");
})
. ServerFiltering(true);
})
.HtmlAttributes( new { style = "margin-left:13px; width: 275px;" })
)
Data access is a little truncated but this is what you'll need to do
#(Html.Kendo().DropDownListFor(model => model.ParentAssetID)
.OptionLabel(" ")
.Name("ParentAssetID")
.DataTextField("AssetName")
.DataValueField("AssetId")
.SelectedIndex(0)
.Text(string.Empty)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetAllAssetsByCompanyId", "Asset");
});
}))
Only a minor change but have you tried read.Action? Also is maybe try removing the following;
DropDownListFor(model => model.ParentAssetID)
and replace with
DropDownListFor<ClassName>()
Only a thought.
How can I pass arguments to CGridView upon it's afterAjaxUpdate function? Currently, I manually set some values in HTML(in hiddent fields) and then they are read from grid's function afterAjaxUpdate.
This does work, but it doesn't sound professional. Any advice?
Here is an example of my "view" code:
<div style="width:<?php $asDialog==1?'700px':'800px' ?>;">
<?php
$appartmentUpdateLink = CJavaScript::encode($this->createUrl('appartment/update', array('id'=>'')));
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'appartment-grid-milimeters-editable',
'dataProvider'=>$model->search3(),
'cssFile'=>Html::cssUrl('gridstyles'),
'columns'=>array(
array( //needed for keyboard arrow up/down binding scripts.
'id' => 'selectedIds',
'class' => 'CCheckBoxColumn',
'checkBoxHtmlOptions'=>array('tabindex'=>'-1'),
),
array(
'name'=>'number',
'class' => 'phaEditColumn2',
'actionUrl' => array('appartment/setAttribute', 'attribute'=>'number'),
'htmlOptions'=>array('width'=>'15px'),
),
array(
'name'=>'name',
'header'=>'Διαμ.',
'class' => 'phaEditColumn2',
'actionUrl' => array('appartment/setAttribute', 'attribute'=>'name'),
'htmlOptions'=>array('width'=>'25px'),
),
),
'afterAjaxUpdate' => 'function(id, data) {
var foundOnPage = false;
var erroredIdsString = $("#recentIdDialogForError").text();
var erroredIdsArray = erroredIdsString.split(",");
var errorsLength = erroredIdsArray.length;
//reset errored rows (red outline).
$("#"+id).children(".items").children("tbody").find("input.select-on-check").closest("tr").css("outline", "0px");
$("#"+id).children(".items").children("tbody").find("input.select-on-check").each(function () {
for(var i=0; i<errorsLength; i++){
if($(this).prop("value") == erroredIdsArray[i]){
$(this).closest("tr").css("outline", "1px solid red");
}
}
if($(this).prop("value") == $("#recentIdDialog").text()){ //return to page after normal create/update
foundOnPage = true;
$(this).closest("tr").addClass("selected");
$(this).prop("checked", true);
$(".appartmentUpdateButton").prop("disabled", false);
$(".appartmentUpdateButton").parent().attr({
href: '. $appartmentUpdateLink .'+$("#recentIdDialog").text()+"?returnPage=2",
});
} //if
else if($(this).prop("value") == $("#recentIdDialogForNext").text()){ //just updated record(s) inline at the editable grid
if(!$(this).closest("tr").is(":last-child")){
var nextTr = $(this).closest("tr").next();
foundOnPage = true;
$(nextTr).addClass("selected");
$(nextTr).find("input.select-on-check").prop("checked", true);
$(nextTr).find(".button-column a.update").trigger("click");
$(".appartmentUpdateButton").prop("disabled", false);
$(".appartmentUpdateButton").parent().attr({
href: '. $appartmentUpdateLink .'+$(nextTr).find("input.select-on-check").prop("value")+"?returnPage=2",
});
}else{ //it is the last child -- go add a new record!
$(".newButton").trigger("click");
}
}//else if
if(foundOnPage == false){
$(".appartmentUpdateButton").prop("disabled", true);
$(".appartmentUpdateButton").parent().removeAttr("href");
}
?>
</div>
<div id='recentIdDialog' style='display:none'></div>
<div id='recentIdDialogForNext' style='display:none'></div>
<div id='recentIdDialogForNextSoft' style='display:none'></div>
<div id='recentIdDialogForError' style='display:none'></div>
<?php echo CHtml::link(CHtml::button('Διόρθωση', array('disabled'=>'disabled', 'id'=>'appartmentUpdateButton2', 'class'=>'appartmentUpdateButton', 'style'=>'width:120px')),array('appartment/update', 'id'=>''));?>
and one of the "view" scripts:
function sendData3(myUrl, dataObject){
return $.ajax({
type: "POST",
cache: false,
url: myUrl,
data: {myData: dataObject},
success: function(answer){
var obj = $.parseJSON(answer);
var successArray = obj.success;
var failureArray = obj.failure;
var printableAnswer = "";
if(successArray.length > 0){
printableAnswer += obj.msgSuccess;
$("#recentIdDialog").html("");
$("#recentIdDialogForNext").html(successArray[successArray.length-1]);
$("#recentIdDialogForNextSoft").html("");
$("recentIdDialogForError").html("");
}
if(failureArray.length > 0){
printableAnswer += obj.msgFailure;
var errors = "";
$("recentIdDialogForError").html("");
$.each(obj.errors, function(key, val) {
errors += "<div class=\"flash-error\">" + val + "</div>";
});
printableAnswer += errors;
$("#recentIdDialog").html((obj.failure)[0]); //FIRST errored line of the grid remains selected.
$("#recentIdDialogForNext").html("");
$("#recentIdDialogForNextSoft").html("");
for(var j=0; j<failureArray.length; j++){
$("#recentIdDialogForError").append(failureArray[j] + ",");
}
}
$("#statusMsgDialog").html(printableAnswer);
$("#statusMsgDialog").show();
if(failureArray.length <= 0){
$("#statusMsgDialog").delay(7000).fadeOut("slow");
}
},
});
}
And the controller:
$dataObject = $_POST['myData'];
$item = $dataObject['item'];
$model=$this->loadModel($item);
$model->attributes = $dataObject;
$valid = $model->validate();
if($valid){
if($model->save()){
$answer = array(
'status'=>'success',
'id'=>$model->id,
'msg'=>"<div class='flash-success'>Ajax - success [".($model->name?$model->name:$model->id)."].</div>");
echo json_encode($answer);
Yii::app()->end();
}
}else{
$error = CActiveForm::validate($model);
$errorArray = json_decode($error, true);
$answer = array(
'status'=>'error',
'id'=>$model->id,
'msg'=>"<div class='flash-error'>Ajax - error [".($model->name?$model->name:$model->id)."].</div>",
'errors'=>$errorArray);
echo json_encode($answer);
Yii::app()->end();
}