I have been stumped on this for about 2 hours now.
My problem is that I need to load an array from a MySQL database using PHP and Ajax, and use the array in JavaScript.
I got that part working fine however the part where it references "onClick" and contains a function to run does not work. It provides numerous errors which say the exact same thing.
Uncaught RangeError: Maximum call stack size exceeded
at buttons.<computed>.onClick (app.js:1281)
The example of the array is the following:
[
{
"text": "Lost to Competitor",
"onClick": "closeOpportunity(\"Lost to Competitor\", el, stages)"
},
{
"text": "No Budget \/ Lost Funding",
"onClick": "closeOpportunity(\"No Budget \/ Lost Funding\", el, stages)"
},
{
"text": "No Decision \/ Non-responsive",
"onClick": "closeOpportunity(\"No Decision \/ Non-responsive\", el, stages)"
},
{
"text": "Price",
"onClick": "closeOpportunity(\"Price\", el, stages)"
},
{
"text": "Other",
"onClick": "closeOpportunity(\"Other\", el, stages)"
},
{
"text": "Won via Another Opportunity",
"onClick": "closeOpportunity(\"Won via Another Opportunity\", el, stages)"
}
]
My code for loading the array is the following:
function closeOpportunity(name, el, stages) {
$$("#opportunity_loss_reason2").text(name);
$$("#closedType").text("Closed Lost");
$$("#convertToProject").hide();
$$("#lostReasonLI").show();
upStepper(el, stages);
}
var stages = [
"enquiry",
"qualification",
"proposal",
"negotiation",
"closed"
];
var buttons = [];
app.request.json('scripts/lostButtonsArray.php', function (data) {
buttons = data;
console.log(buttons);
});
buttons.forEach((v, i) => {
console.log(v['onClick']);
buttons[i]['onClick'] = function() { window.eval.call(window, v['onClick'])(el, stages); };
});
app.dialog.create({
title: 'ECOM',
text: 'Why was the opportunity lost?',
cssClass: 'custom-dialog',
closeByBackdropClick: 'true',
buttons: buttons,
verticalButtons: true,
}).open();
I have already tried using regular eval() and loading the code directly without any sort of helper (eval, window.eval).
I will be happy to provide more information to help solve this problem if I haven't provided enough information.
Found the solution.
I was trying to load "name" instead of "text"
Working function
app.request.json('scripts/lostButtonsArray.php', function (data) {
buttons = data;
buttons.forEach((v, i) => {
buttons[i]['onClick'] = function() { eval('closeOpportunity')(v['text'], el, stages); };
});
});
Related
I have a Kendo jquery AutoComplete UI component with a remote REST web API dataSource. I want to map the responce of the API to the autoComplete. I have it displaying the choices but when I examine the select function I just see what I've selected. What I want is the complete json object on the select event. Or at least what I really need is the unitKey property.
my code:
//create AutoComplete UI component
$("#autoAddress").kendoAutoComplete({
minLength: 3,
dataTextField: "address",
filter: "startswith",
placeholder: "Address lookup ...",
select : function(e) {
var item = e.item;
console.log(item.text());
},
dataSource: new kendo.data.DataSource ({
serverFiltering: true,
transport: {
read: {
url: "https://testapi.mydomain.com/homeinfo/api/address/",
// dataType: "jsonp",
data: {
q : function() {
return $("#autoAddress").data("kendoAutoComplete").value();
},
maxRows: 30
}
}
},
schema : {
data: function(response){
return response;
}
}
})
});
sample date from api call:
[
{
"unitKey": "S37.75 ",
"address": "1234 ADDISON AVE"
},
{
"unitKey": "S22.215 ",
"address": "1234 AUGUSTINE DR"
},
{
"unitKey": "L100.9 ",
"address": "1234 AVENIDA DE LAS CASAS"
}
]
I'm trying to get the "unitKey" when the user makes a selection from the autocomplete component.
You want to look at e.dataItem rather than e.item:
console.log(e.dataItem.unitKey);
In cases like this I usually log 'e' itself to the console or breakpoint with the debugger to inspect it to see what it contains since the documentation is not always as comprehensive as it could be.
I am building my first application with React.js.
The application uses external movie API (https://www.themoviedb.org/documentation/api) to search and list movies.
The request takes parameters "query" and optionally "page". The response already contains total number of results and total number of existing pages.
The sample response looks something like this:
{
"page": 1,
"results": [
{
"adult": false,
"backdrop_path": "/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg",
"genre_ids": [18],
"id": 550,
"original_language": "en",
"original_title": "Fight Club",
"overview": "A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground \"fight clubs\" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.",
"release_date": "1999-10-14",
"poster_path": "/811DjJTon9gD6hZ8nCjSitaIXFQ.jpg",
"popularity": 4.39844,
"title": "Fight Club",
"video": false,
"vote_average": 7.8,
"vote_count": 3527
},
{
"adult": false,
"backdrop_path": "/qrZssI8koUdRxkYnrOKMRY3m5Fq.jpg",
"genre_ids": [
27
],
"id": 289732,
"original_language": "zh",
"original_title": "Zombie Fight Club",
"overview": "It's the end of the century at a corner of the city in a building riddled with crime - Everyone in the building has turned into zombies. After Jenny's boyfriend is killed in a zombie attack, she faces the challenge of surviving in the face of adversity. In order to stay alive, she struggles with Andy to flee danger.",
"release_date": "2014-10-23",
"poster_path": "/7k9db7pJyTaVbz3G4eshGltivR1.jpg",
"popularity": 1.621746,
"title": "Zombie Fight Club",
"video": false,
"vote_average": 3.5,
"vote_count": 2
}
],
"total_pages": 1,
"total_results": 2
}
The structure, that I have picked is the following:
MovieSearchPage
- SearchForm
- SearchResults
--MovieBox
--Pagination
---Page
Here's my solution, which almost works fine:
var SearchResults = React.createClass({
handlePageChange: function(pageNum) {
this.props.onPageChange(pageNum);
},
render: function() {
var movies = [];
this.props.data.results.forEach(function(movie) {
movies.push(<MovieBox movie={movie} key={movie.id}/>);
});
return (
<div>
<h3>Search results ({this.props.data.total_results} found)</h3>
<Pagination onPageClick={this.handlePageChange} currentPage={this.props.data.page} totalPages={this.props.data.total_pages} totalResults={this.props.data.total_results}/>
<div>{movies}</div>
</div>
);
}
});
var SearchBar = React.createClass({
getInitialState: function() {
return {query: ''};
},
handleQueryChange: function(e) {
this.setState({query: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var query = this.state.query.trim();
if (!query || query.length < 3) {
return;
}
this.props.onSearch(query);
},
render: function() {
return (
<form className="searchForm" onSubmit={this.handleSubmit}>
<input
type="text"
name="query"
placeholder="Search for a movie..."
value={this.state.query}
onChange={this.handleQueryChange}
/>
<input type="submit" value="Search Movie"/>
</form>
);
}
});
var MovieBox = React.createClass({
render: function() {
var m = this.props.movie;
return (
<div className="movieBox">
<div>{m.original_title} ({m.release_date})</div>
</div>
);
}
});
var Pagination = React.createClass({
render: function() {
var pages = [];
var total = this.props.totalPages;
for (var i = 1; i <= total; i++) {
pages.push(<Page key={i} onPageClick={this.props.onPageClick} pageNum={i} active={(this.props.currentPage == i) || false}/>);
}
return (
<div className="pagination">{pages}</div>
);
}
});
var Page = React.createClass({
handleClick: function(e) {
var pageNum = e.target.innerHTML;
this.props.onPageClick(pageNum);
},
render: function() {
return (
<a href="#" onClick={this.handleClick} className={this.props.active ? 'active' : ''}>{this.props.pageNum}</a>
)
}
});
var MovieSearchPage = React.createClass({
getInitialState: function() {
return {query: '', pageNum: 1, data: {results: [], total_pages: 0, total_results: 0}};
},
initSearch: function(query) {
this.setState({query: query});
this.doSearch(query, this.state.pageNum);
},
nextPage: function(pageNum) {
this.setState({pageNum: pageNum});
this.doSearch(this.state.query, pageNum);
},
doSearch: function(query, pageNum) {
$.ajax({
url: this.props.url,
dataType : 'json',
type: 'POST',
data: {query: query, api_key: this.props.apiKey, page: pageNum},
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div>
<SearchBar onSearch={this.initSearch}/>
<SearchResults onPageChange={this.nextPage} data={this.state.data}/>
</div>
);
}
});
ReactDOM.render(
<MovieSearchPage url="http://api.themoviedb.org/3/search/movie" apiKey=""/>,
document.getElementById('container')
);
The problem, that I am having here, is that initial search is performed within SearchForm, using "query" state, however, I want to perform search requests to API from clicking on pagination (same query string, but different page number) without reloading the page.
I managed to achieve this by passing relevant parent method from MovieSearchPage all the way down to Page component.
This works fine, but I firmly believe, that this is not the most elegant solution to do this - the same method is passed multiple times plus I had to duplicate "query" state in both MovieSearchBox and SearchForm.
Please forgive me being a noob, but this is really the first time I am using a complex front-end framework. What is the proper way to do it in order to achieve this?
Despite situation being very generic, I was not able to find anything on the web.
Thanks in advance!
Im trying to find a way I can suppress the changedevent in jstree when loading a dynamic context menu (right click). I'm aware that you can suppress the select_node event in the context menu but I need to get the node id of the node that I am right clicking on. (and therefore need to use the select_node). I know that you can suppress that changed event when calling select_node regularly, but I'm not sure how to do it when right clicking. I tried the following with the context menu select_node, but it did not work:
$(function () {
$('#myTree').jstree({
"core": {
"themes": {
"variant": "small",
"icons": false
}
},
"contextmenu": {
"items": reportMenu(node), //builds context menu based on selected node
},
"plugins": ["contextmenu", "changed"]
});
});
$("#myTree").bind('select_node.jstree', function (event, data) {
// Does not work, changed event still fires.
$("#myTree").jstree().select_node(data.node.id, true);
});
I'm looking for one of the possible alternatives:
How can I suppress the changedevent when the context menu calls select_node?
How can I get the id of the node I am right clicking on without calling the select_node event (i.e. If I set my contextmenu to 'select_node': false, how can I capture the select node)?
Finally, I think you can get what you want changing your code a little.
Check demo - codepen.
$('#myTree')
.jstree({
'core': {
'data': ...
},
'plugins': ["contextmenu"],
'contextmenu': {
'select_node': false,
'items': reportMenu
}
});
function reportMenu(node) {
// access node as: node.id);
// build your menu depending on node id
return {
createItem: {
"label": "Create New Branch",
"action": function(obj) {
this.create(obj);
alert(obj.text())
},
"_class": "class"
},
renameItem: {
"label": "Rename Branch",
"action": function(obj) { this.rename(obj); }
},
deleteItem: {
"label": "Remove Branch",
"action": function(obj) { this.remove(obj); }
}
};
}
In the previous versions one can easily get this done by:
someDynamicData=function(){
//return some data...
}
$("#lga").select2({
width: '100%',
allowClear: false, placeholder: "--- please select ---",
data: function () {
return {results: someDynamicData, text: 'name'};
}
});
I this dosen't work with the new select2 v4. How do I achieve this in the new select2 v4.
For anyone that might be stocked as much as I am with select2 v4. I got a workaroud. Please note that I am some javascript guru and there might be some better way around this issue. For the time being this is rocking:
Case study: I have a chained select options which share from a list JSON Objects: here I am show only Subject and Level example just to keep it simple. When a Subject is selected I what to initialize the Level data source from the selected Subject's data source
function getDynamicData(){
return [
{"code": "ACC", "name": "ACCOUNTING", "levels": [
{"id": 3, "name": "SS 1", "description": "Senior Secondary School 1", "type": "Standard", "resultTemplates": []},
....
]}
....
];
}
$.fn.select2.amd.require(['select2/data/array', 'select2/utils'],
function (ArrayData, Utils) {
function CustomData($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
subjects = getDynamicData();
$.map(subjects, function (d) {//Required only when the object do not have either id/text
d.id = d.code;
d.text = d.name;
return d;
});
callback({results: subjects});
};
$("#subjects").select2({
width: '100%',
dataAdapter: CustomData
});
}
);
This works fine but then I realized that I am repeating many things hence it is time for cleanup
//Added this function to a javascript file on my template (shared across pages)
function initMySelect2(myQuery, mySelector, props) {
$.fn.select2.amd.require(['select2/data/array', 'select2/utils'],
function (ArrayData, Utils) {
function CustomData($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = myQuery;//Pass the query function as a parameter
$(mySelector).select2(props(CustomData));//Initialise the select2 with the custom data
}
);
}
//Used this to initialize select2
initMySelect2(function (params, callback) {
callback({results: getDynamicData()});
},
"#subjects",
function (myCustomData) {
return {
width: '100%',
placeholder: '---please select subjects ---',
dataAdapter: myCustomData
};
}
);
Reference https://select2.github.io/announcements-4.0.html
I hope this will help someone out there.
i have been trying to figure this out lately but i can't
the problem is that i have an input field with type text that i need to get the current input data when the values from the autocomplete are selected. Note i am using jQuery UI autocomplete.
i can catch the keyup event but when a user uses clicks on the autocomplete values. jQuery does not fire the change event handler, i tried using every event handler there is but to no avail.
i think it cannot catch a DOM based manipulation of an element? i'm not sure. here is a
fiddle
Like this http://jsfiddle.net/PUpRr/
select options should do the trick.
Options/events/methods API documentation : http://api.jqueryui.com/autocomplete/
Hope this fits the needs :)
Sample code
$("#to").autocomplete({
source: function (request, response) {
var friendsArray = [];
friendsArray = [{
"id": 1,
"name": "hulk",
"value": "hulk"
}, {
"id": 2,
"name": "ironman",
"value": "ironman"
}, {
"id": 3,
"name": "Foobar",
"value": "Foobar"
}];
response(friendsArray);
return;
},
select: function (e, ui) {
alert("selected!");
},
change: function (e, ui) {
alert("changed!");
}
});
Chrome had issues retaining the ui for clicked changes, so I added a mousedown handler for the individual popup list anchor tags in the autocomplete open: event. It's also a good place for styling the list:
function onItemTypeAheadListOpen(e, ui) {
// Stub for list click issues
$('.ui-autocomplete a').mousedown(function () {
lastItemClicked = this.innerText;
});
// Override default list style
$('.ui-autocomplete').css('z-index', '600');
$('.ui-autocomplete').css('width', '480px');
}