I have a controller that gets data from database and turns it in events for fullcalendar, when I display a modal clicking in one of this events, it shows that description and email are undefined.
Image of modal undefined fields
But in XHR I can see that im receiving all the data from the controller.
Image of XHR GET
Modal:
<div id="fullCalModal" class="modal fade" style="z-index: 9999;>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 id="modalTitle" class="modal-title"></h4>
</div>
<div id="modalBody" class="modal-body">
<p id="ev_start" class="modal-body"></p>
<p id="ev_end" class="modal-body"></p>
<p id="ev_mail" class="modal-body"></p>
<p id="ev_desc" class="modal-body"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button class="btn btn-primary"><a id="eventUrl" target="_blank">Event Page</a></button>
</div>
</div>
</div>
Javascript:
eventClick: function(info) {
var eventObj = info.event;
alert('Clicked ' + eventObj.title + ' with id: ' + eventObj.id + eventObj.description);
$('#modalTitle').html(eventObj.title);
$('#ev_titulo').html(eventObj.title);
$('#ev_start').html('Fecha inicio: ' + eventObj.start);
$('#ev_end').html('Fecha fin: ' + eventObj.end);
$('#ev_desc').html('Descripcion: ' + eventObj.description);
$('#ev_mail').html('Mail: ' + eventObj.mail);
$('#fullCalModal').modal('show');
},
Controller:
public function loadAction()
{
$em = $this->getDoctrine()->getManager();
$eventos = $em->getRepository('App:Evento')->findAll();
$data = array();
foreach ($eventos as $evento)
{
$events['id'] = $evento->getId();
$events['title'] = $evento->getTitle();
$events['start'] = $evento->getBeginAt()->format('Y-m-d');;
$events['end'] = $evento->getEndAt()->format('Y-m-d');;
$events['color'] = $evento->getColor();
$events['description'] = $evento->getDescription();
$events['mail'] = $evento->getMail();
array_push($data, $events);
}
return $this->json($data);
}
The event parsing documentation states that
Every other non-standard prop will be transferred over to the
extendedProps hash in the Event Object.
Since description and mail are not standard event properties in fullCalendar (as listed in that documentation), then they will be placed under the "extendedProps" object in the final event object which fullCalendar creates based on the data it receives from your server.
Therefore in your eventClick code you should be able to write
$('#ev_desc').html('Descripcion: ' + eventObj.extendedProps.description);
$('#ev_mail').html('Mail: ' + eventObj.extendedProps.mail);
and populate the properties successfully into your modal.
P.S. This behaviour is also mentioned in the event object documentation as well. If you have previously used fullCalendar version 3 or below, then this is a change from how these earlier versions worked.
Related
Explanation:
I have an ASP.NET application, where I can retrieve Vehicle information with a click of a button. The Vehicle detail is then displayed in a modal screen with the following data: ID, VEHICLENAME,YEAR, and VEHICLE_DOCUMENTATION.
The VEHICLE_DOCUMENTATION is displayed in anchor tags, which is created using JavaScript. The expectation is when I retrieve Vehicle detail, their relevant VEHICLE_DOCUMENTATION would be displayed, and created in an anchor tag. If another Vehicle detail is retrieved, their relevant VEHICLE_DOCUMENTATION would also be created in an anchor tag.
The problem I'm encountering is when anchor tag are created for a Vehicle detail, I can not create anchor tags for the next time I retrieve another Vehicle detail. I have outlined the errors I keep getting below.
I've provided the full working code below, including the error message I encounter. Hopefully someone could assist me.
DetailController:
DetailController:
[HttpPost]
public ActionResult GetDetail(int? id)
{
return _context.Vehicle.Where(v => v.ID == id).Select(x => new VehicleModel(){
ID = x.ID,
VEHICLENAME = x.VEHICLENAME,
YEAR = x.YEAR,
VEHICLE_DOCUMENTATION = x.VEHICLE_DOCUMENTATION
}).FirstOrDefault();
}
Detail Razor Page:
Button
------
<button type="button" data-toggle="modal" data-target="#detailModal" onclick="postData(id)"></button>
Modal window appears when button is clicked
-------------------------------------------
<div class="modal fade" id="detailModal" tabindex="-1" role="dialog" aria-labelledby="detailModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="detailModalTitle">Vehicle Detail</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
ID: <label id="ID"></label><br />
VEHICLE NAME: <label id="VEHICLENAME"></label><br />
YEAR: <label id="YEAR"></label><br />
VEHICLE DOCUMENTATION: <label id="VEHICLE_DOCUMENTATION"></label><br />
</div>
</div>
</div>
</div>
AJAX POST to GetDetail method:
------------------------------
<script>
function postData(id) {
response = '';
$.ajax({
async: true,
type: "POST",
url: "/Detail/GetDetail",
data: { 'id': id},
success: function (xhr) {
response = xhr;
$('#ID').text(response.data.ID);
$('#VEHICLENAME').text(response.data.VEHICLENAME);
$('#YEAR').text(response.data.YEAR);
$('#VEHICLE_DOCUMENTATION').text(response.data.VEHICLE_DOCUMENTATION);
//Create anchor tags
if (response.data.VEHICLE_DOCUMENTATION.length !== 0) {
for (const doc of response.data.VEHICLE_DOCUMENTATION) {
const anchor = document.createElement('a');
const list = document.getElementById('VEHICLE_DOCUMENTATION');
const li = document.createElement('ol');
anchor.href = 'www.vehicledocumentationexample.com';
anchor.innerText = `${doc}`;
//THIS IS WHERE THE ERROR IS BEING FLAGGED:
li.appendChild(anchor);
list.appendChild(li);
}
//Delete/Remove elements
} else if (response.data.VEHICLE_DOCUMENTATION.length == 0) {
response.data.VEHICLE_DOCUMENTATION = [];
document.getElementById("VEHICLE_DOCUMENTATION").outerHTML = "";
//WHAT I'VE TRIED:
//document.getElementById('VEHICLE_DOCUMENTATION').remove();
//document.getElementById('VEHICLE_DOCUMENTATION').outerHTML = "";
}
},
error: function (xhr) {
response = xhr;
}
})
}
</script>
Browser error message:
Uncaught TypeError: Cannot read property 'appendChild' of null
at Object.success (...)
at fire (jquery.js:3240)
at Object.fireWith [as resolveWith] (jquery.js:3370)
at done (jquery.js:9061)
at XMLHttpRequest.<anonymous> (jquery.js:9303)
when you put:
document.getElementById("VEHICLE_DOCUMENTATION").outerHTML = "";
What you are doing is to erase the label element, so when you call it from
const list = document.getElementById('VEHICLE_DOCUMENTATION');
It will return null as the element do not exist anymore
Instead of using "outerHTML" you should use "innerHTML", here it is the difference between them:
console.log(document.getElementById("VEHICLE_DOCUMENTATION").outerHTML);
console.log(document.getElementById("VEHICLE_DOCUMENTATION").innerHTML);
<label id="VEHICLE_DOCUMENTATION">
<p>something</p>
</label>
I wrote a function (basing on some other I found on the Internet), which aids in displaying dynamically-generated modals without need to create ones in page's HTML.
To simplify code analysis, briefly:
I add new binding handler to Knockout to allow disabling binding on specific element
Modal is generated from a template using Mustache
Mustache fills in relevant parts of the modal (title, body, buttons)
Modal is wrapped in div, which stops data binding (modalWrapper)
... so that I can apply custom viewmodel to the modal with ko.applyBindings
Buttons are generated automatically basing on description, like:
{
label: "OK",
cssClass: "default",
handler: "handleClick", // sets data-bind="click: handleClick"
autoClose: true // adds data-dismiss="modal"
}
Relevant parts of the code follows:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
var modalTemplate = '<div class="modal fade">\
<div class="modal-dialog {{size}}">\
<div class="modal-content">\
<div class="modal-header">\
<h5 class="modal-title">{{title}}</h5>\
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>\
</div>\
<div class="modal-body">{{&body}}</div>\
<div class="modal-footer">{{&buttons}}</div>\
</div>\
</div>\
</div>';
/**
* Displays modal on the screen.
* #param {Object} options Options
* #param {string} options.title Title of the modal
* #param {html} options.body Body of the modal
* #param {string} options.size Size of the modal. Can be small, default, large or xlarge.
* #param {Object} options.actions Describes buttons to display on the modal. For each, specify label, cssClass, handler and optionally autoClose.
*/
var showModal = function(options) {
options = options || {};
options = $.extend({
title: '',
body: '',
size: false,
actions: false,
viewModel: {}
}, options);
var modalClass = {
small: "modal-sm",
default: "",
large: "modal-lg",
xlarge: "modal-xl"
};
var modalWrapper = $('<div data-bind="stopBinding: true"></div>').appendTo('body');
var buttons;
if (options.actions === false) {
buttons = '<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>';
} else {
buttons = "";
for (var i = 0, len = options.actions.length; i < len; i++) {
var currentButton = $.extend({
label: '<No label>',
cssClass: 'default',
handler: '',
autoClose: true
}, options.actions[i]);
var btn = '<button type="button" class="btn ' +
options.actions[i].cssClass +
'" data-bind="click: ' +
options.actions[i].handler +
(options.actions[i].autoClose === true ? '" data-dismiss="modal"' : '')
+ '>'
+ options.actions[i].label
+ '</button>';
buttons += btn;
}
}
var templateData = {
title: options.title,
body: options.body,
size: modalClass[options.size],
buttons: buttons
};
var modalHtml = Mustache.render(modalTemplate, templateData);
var $modal = $(modalHtml).appendTo(modalWrapper);
$modal.on('hidden.bs.modal', function (e) {
modalWrapper.remove();
});
ko.applyBindings(options.viewModel, $modal.get()[0]);
$modal.modal(options);
};
I have a problem with this autoclosing feature. If it is on, viewmodel processes the click, modal is closed by Bootstrap mechanisms and then removed from DOM after hiding.
But when I want a button to be non-auto-closing, I have no means to close modal from the viewmodel. The solution I thought of was to inject a method to viewmodel like:
viewmodel['close'] = function() { $modal.modal('hide'); };
However this seems to be a hackish solution (even for Javascript ;)). Similarly, I may inject the $modal itself to the viewmodel, but that would be even more ugly.
What would be then the best way to close the modal from within modal's viewmodel?
I solved a similar requirement by assigning an (auto-generated) id to the modal div tag.
var modalId = ('modal' + Math.random()).replace('.', '');
var modalTemplate = '<div class="modal fade" id="' + modalId + '">'\ // remaining code here
Then, that id is being passed to viewmodel, which allows it to close the corresponding modal via
$('#' + _self.modalId).modal('hide');
A comparable event subscription to 'hidden.bs.modal' handles the cleanup.
I would create a custom Knockout binding for handling the visibility of the modal.
This binding connects the bound modal element to the supplied model observable. Click handlers in the model now only have to manage this observable to control the modal visibility.
You could apply this binding for example in your modalTemplate variable: var modalTemplate = `<div class="modal fade" data-bind="bootstrapModalVisible: yourObservable">\...
I haven't incorporated the binding in your case, but below is an example of the working of this binding.
the modalVisible observable is initialized to true, so the modal immediately shows
the close button immediately hides the modal by setting the observable to false
the upper-right close button closes the modal because of data-dismiss="modal". The event handlers in the binding's init will make sure the correct state is written to the observable.
the save button only closes the modal when confirmed, showing the possibility to postpone closing the modal
ko.bindingHandlers['bootstrapModalVisible'] = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).on('hidden.bs.modal', function () { valueAccessor()(false); })
$(element).on('shown.bs.modal', function () { valueAccessor()(true); })
},
update: function (element, valueAccessor) {
if (ko.unwrap(valueAccessor())) {
$(element).modal('show');
} else {
$(element).modal('hide');
}
}
};
function Test() {
var self = this;
self.modalVisible = ko.observable(true);
self.showModal = function() {
self.modalVisible(true);
};
self.hideModal = function() {
self.modalVisible(false);
};
self.save = function() {
if (window.confirm('Close modal?')) {
self.modalVisible(false);
}
}
}
ko.applyBindings(new Test());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<button type="button" class="btn btn-primary" data-bind="click: showModal">
Launch demo modal
</button>
<div class="modal fade" data-bind="bootstrapModalVisible: modalVisible">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Example modal</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bind="click: hideModal">Close</button>
<button type="button" class="btn btn-primary" data-bind="click: save">Save changes</button>
</div>
</div>
</div>
</div>
I'm currently using AJAX with Django Framework.
I can pass asynchronous POST/GET to Django, and let it return a json object.
Then according to the result passed from Django, I will loop through the data, and update a table on the webpage.
The HTML for the table:
<!-- Modal for Variable Search-->
<div class="modal fade" id="variableSearch" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Variable Name Search</h4>
</div>
<div class="modal-body">
<table id="variableSearchTable" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th> Variable Name </th>
</tr>
</thead>
</table>
<p>
<div class="progress">
<div class="progress-bar progress-bar-striped active" id="variableSearchProgressBar" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
<span class="sr-only">0% Complete</span>
</div>
</div>
</p>
<p>
<div class="row">
<div class="col-lg-10">
<button class="btn btn-default" type="button" id="addSearchVariable" >Add</button>
</div>
</div>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" id="variableSearchDataCloseButton" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Basically it is a bootstrap 3 modal, with jQuery DataTable, and with a progress bar to show the user the current progress.
The Javascript that is used to get Django results:
$('#chartSearchVariable').click(function(event)
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("show");
var csrftoken = getCookie('csrftoken');
var blockname = document.getElementById('chartConfigModalBlockname').value;
$('#variableSearchProgressBar').css('width', "0%").attr('aria-valuenow', "0%");
event.preventDefault();
$.ajax(
{
type:"GET",
url:"ajax_retreiveVariableNames/",
timeout: 4000000,
data:
{
'csrfmiddlewaretoken':csrftoken,
'blockname':blockname
},
success: function(response)
{
if(response.status == "invalid")
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("hide");
$('#invalid').modal("show");
}
else
{
configurationVariableChart.row('').remove().draw(false);
for (i = 0 ; i < response.variables.length; i++)
{
configurationVariableChart.row.add(
$(
'<tr>' +
'<td>' + response.variables[i] + '</td>' +
'<tr>'
)[0]);
}
configurationVariableChart.draw();
$('#variableSearchProgressBar').css('width', "100%").attr('aria-valuenow', "100%");
}
},
failure: function(response)
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("hide");
$('#invalid').modal("show");
}
});
return false;
});
$('#addSearchVariable').click(function(event)
{
$('#variableSearch').modal("hide");
$('#chartConfigModal').modal("show");
document.getElementById('chartConfigModalVariable').value = currentVariableNameSelects;
});
$('#variableSearchDataCloseButton').click(function(event)
{
$('#variableSearch').modal("hide");
$('#chartConfigModal').modal("show");
});
The problem is with the updating table part:
configurationVariableChart.row('').remove().draw(false);
for (i = 0 ; i < response.variables.length; i++)
{
configurationVariableChart.row.add(
$(
'<tr>' +
'<td>' + response.variables[i] + '</td>' +
'<tr>'
)[0]);
}
configurationVariableChart.draw();
$('#variableSearchProgressBar').css('width', "100%").attr('aria-valuenow', "100%");
Since the response.variables can be over 10k, and it will freeze the web browser, even though it is still drawing.
I'm pretty new to Web Design (less than 4 months), but I assume it's because they are all running on the same thread.
Is there a way in Javascript to do threading/async? I had a search, and the results were deferred/promise which seems very abstract at the moment.
Try processing retrieved data incrementally.
At piece below , elements generated in blocks of 250 , primarily utilizing jQuery deferred.notify() and deferred.progress().
When all 10,000 items processed , the deferred object is resolved with the collection of 10,000 elements. The elements are then added to document at single call to .html() within deferred.then()'s .done() callback ; .fail() callback cast as null .
Alternatively , could append elements to the document in blocks of 250 , within deferred.progress callback ; instead of at the single call within deferred.done , which occurs upon completion of the entire task.
setTimeout is utilized to prevent "freeze the web browser" condition .
$(function() {
// 10k items
var arr = $.map(new Array(10000), function(v, k) {
return v === undefined ? k : null
});
var len = arr.length;
var dfd = new $.Deferred();
// collection of items processed at `for` loop in blocks of 250
var fragment = [];
var redraw = function() {
for (i = 0 ; i < 250; i++)
{
// configurationVariableChart.row.add(
// $(
fragment.push('<tr>' +
'<td>' + arr[i] + '</td>' +
'</tr>')
// )[0]);
};
arr.splice(0, 250);
console.log(fragment, arr, arr.length);
return dfd.notify([arr, fragment])
};
$.when(redraw())
// `done` callbacks
.then(function(data) {
$("#results").html(data.join(","));
delete fragment;
}
// `fail` callbacks
, null
// `progress` callbacks
, function(data) {
// log , display `progress` of tasks
console.log(data);
$("progress").val(data[1].length);
$("output:first").text(Math.floor(data[1].length / 100) + "%");
$("output:last").text(data[1].length +" of "+ len + " items processed");
$("#results").html("processing data...");
if (data[0].length) {
var s = setTimeout(function() {
redraw()
}, 100)
} else {
clearTimeout(s);
dfd.resolve(data[1]);
}
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<progress min="0" max="10000"></progress><output for="progress"></output>
<output for="progress"></output><br />
<table id="results"></table>
jsfiddle http://jsfiddle.net/guest271314/ess28zLh/
Deferreds/promises won't help you here. JS in the browser is always single-threaded.
The trick is not to build up DOM elements via JS. That is always going to be expensive and slow. Rather than passing data in JSON from Django and building up a DOM dynamically, you should get Django to render a template fragment on the server side and pass that whole thing to the front-end, where the JS can simply insert it at the relevant point.
I have no clue how to even begin explaining this situation but I'll try my best. I have a simple Spanish-English-Spanish dictionary lookup page with a text box, a Lookup button, and a div to show the results. When you enter a word to lookup in the text box and hit Lookup, the results are shown in the div below.
In the results some words are hyperlinked so when you click on them, you get the search result for the clicked word in the div. That's just like any online dictionary service functions. It works perfect except that the second functionality doesn't seem to work on the first click after a typed search. For example:
You type pedir in the input box and hit Lookup. The div below now shows the detailed meaning of pedir including hyperlinked words like ask, English for pedir. Now, you click ask which should refresh the div and show you the Spanish meanings of ask including words like pedir. However, it just refreshes the div and shows the same content as if you looked up pedir a second time. But when you click on ask a second time now, it works fine as expected. It must be noted that the words are hyperlinked appropriately and there's no mis-linking going on here. Not only that, other links (such as the ones on the navigation tab on top) also don't seem to work on first click. This happens every time a new word is looked up.
Hope the above example illustrates the problem well enough; at least that's what I have tried. My routing and controllers look like this:
var asApp = angular.module('asApp', ['ngRoute']);
asApp.config(function($routeProvider) {
$routeProvider
.when('/', {
title: 'Home of thesite – Radical Spanish learning tips and tricks for the adventurous learner',
templateUrl : 'pages/home.html',
controller : 'mainController'
})
// route for dictionary
.when('/dictionary', {
title: 'The dictionary',
templateUrl : 'pages/dictionary.html',
controller : 'mainController'
})
// route for dictionary term
.when('/dictionary/:word2lookup', {
title: 'The thesite dictionary',
templateUrl : 'pages/dictionary.html',
controller : 'dictController'
})
// route otherwise
.otherwise({
title: 'thesite – Radical Spanish learning tips and tricks for the adventurous learner',
templateUrl : 'pages/home.html',
controller : 'mainController'
});
});
function HeaderController($scope, $location)
{
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}
asApp.run(['$rootScope', '$route', '$location', function($rootScope, $route, $location) {
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
document.title = 'Translation of ' + $route.current.params['word2lookup'] + ' | ' + $route.current.title;
});
}]);
asApp.controller('mainController', function($scope) {});
asApp.controller('dictController', function($scope, $routeParams){});
I don't even know if I can reproduce the entire situation in a fiddle since it involves some significant server-side scripting.
Please let me know if there's anything I could explain in order for someone to identify the gremlin breaking down my code's functionality.
P.S.: This issue only affects the first click (on any link on the page) after a new search has been performed, i.e. a word is entered in the input box and the Lookup button is clicked.
Update: In response to #gr3g's request, here's the code for the functions lookup_check() and lookup_word():
function lookup_check(lookupterm){
close_kb();
if(lookupterm != ""){
lookup_word(lookupterm);
}
else{
var lookup_box = $('#word');
lookup_box.addClass('empty');
setTimeout(function(){ lookup_box.removeClass('empty'); },500);
}
}
// Query dictionary and populate meaning div
function lookup_word(lookupword){
var mean = document.getElementById('meaning');
var waittext = '<div class="preloader-image"><br /><br />';
var hr = createXMLHTTPRequestObject();
var url = 'bootstrap/php/dictengine.php';
var vars = "lookup_word=" + lookupword;
document.getElementById('word').value = lookupword;
hr.open("POST", url, true);
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
hr.onreadystatechange = function(){
if(hr.readyState == 4 && hr.status == 200){
var return_data = hr.responseText;
mean.innerHTML = return_data;
if ($(".el")[0]){ hist_mean = $('.el:first').text(); }
else { hist_mean = ""; }
add2local(lookupword, hist_mean);
$(".tab-container").addClass("hide-tabs");
if($("#dict_span").length != 0) {
$(".tab-container").removeClass("hide-tabs");
// logic to seggregate spanish and english results
$("#dict_eng").addClass("hide-div");
$("#sp_tab").addClass("active");
$("#en_tab").removeClass("active");
}
document.title = 'Translation of ' + lookupword + ' | The thesite dictionary';
$("<hr class='dict-divider'>").insertAfter(".gram_cat");
$("<hr class='dict-divider'>").insertAfter(".quickdef");
$("<hr class='dict-divider'>").insertBefore(".dict_source");
$('div.entry_pos').wrap('<div class="pos"></div>');
$('a.dictionary-neodict-first-part-of-speech').wrap('<div class="pos"></div>');
// update url
var loc = window.location.href;
var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
if(lastpart == 'dictionary'){ window.location.replace(window.location.href + "/" + encodeURI(lookupword)); }
if((lastpart != 'dictionary') && (lastpart != encodeURI(lookupword))){
var addr = window.location.href;
var addrtemp = addr.substring(addr.lastIndexOf('/') + 1);
addr = addr.replace(addrtemp, encodeURI(lookupword));
if(!!(window.history && history.pushState)){ history.pushState(null, null, addr); }
else{ window.location.replace(addr); }
}
}
//else { setTimeout('lookup_word(lookupword)', 1000); }
}
hr.send(vars);
mean.innerHTML = waittext;
}
Update 2: To further facilitate #gr3g, here's dictionary.html:
<!-- dictionary.html -->
<script>
var loc = window.location.href;
var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
if(lastpart != 'dictionary'){ lookup_check(decodeURI(lastpart)); }
// populate search history if available
var recent = document.getElementById('recent-lookups');
var value = localStorage.getItem('w');
if (value) {
value = JSON.parse(value);
var len = value.length - 1;
var str = "";
for (a=len; a>=0; a--){
term = value[a].substr(0, value[a].indexOf('$'));
term_meaning = value[a].substr(value[a].indexOf("$") + 1);
if(term_meaning != "") {
str = str + "<p><strong><a href='/a-s/#/dictionary/" + encodeURI(term) + "'>" + term + "</a></strong> <i class='fa fa-chevron-right' style='color: #a5a5a5;font-size: 80%;'></i> <span class='recent_meanings'>" + term_meaning + "</span></p>";
}
else { str = str + "<p><em>" + term + "</em></p>"; }
}
recent.innerHTML = str;
}
else { recent.innerHTML = "<p>No historical data to show right now. Words will start appearing here as you begin your lookups.</p>"; }
// populate word of the day on pageload
wotd();
</script>
<!-- top-image start -->
<div class="page-header-line-div">
</div>
<!-- top-image end -->
<br>
<br>
<div class="container-fluid" ng-controller="luController as luCtrl">
<div class="row row-padding">
<form class="form-horizontal" role="form" name="lookup-form" id="lookup-form" action="" method="">
<div class="input-group col-md-6">
<input id="word" type="textbox" placeholder="Enter a Spanish or English word here..." class="form-control input-lg lookup-field lookup-field-single" onMouseOver="$(this).focus();" required ng-model="luCtrl.lookuptrm">
<i class="fa fa-times fa-lg delete-icon" onfocus="clearword();" onclick="clearword();" data-toggle="tooltip" data-placement="top" title="Click to clear entered text"></i>
<i class="fa fa-keyboard-o fa-2x kb-icon" onfocus="toggler('virtualkeypad', this);" onclick="toggler('virtualkeypad', this);" data-toggle="tooltip" data-placement="top" title="Click to enter accented characters"></i>
<div class="input-group-btn">
<button class="btn btn-lg btn-primary lookup-submit" type="submit" id="lookup" ng-click="luCtrl.handlelookup(luCtrl.lookuptrm)">Lookup</button>
</div>
</div>
<div id="virtualkeypad" class="btn-group vkb-hide"><!--col-md-offset-4-->
<button class="btn btn-lg first-btn" type="button" onClick="spl_character('á');">á</button>
<button class="btn btn-lg" type="button" onClick="spl_character('é');">é</button>
<button class="btn btn-lg" type="button" onClick="spl_character('í');">í</button>
<button class="btn btn-lg" type="button" onClick="spl_character('ó');">ó</button>
<button class="btn btn-lg" type="button" onClick="spl_character('ú');">ú</button>
<button class="btn btn-lg" type="button" onClick="spl_character('ü');">ü</button>
<button class="btn btn-lg last-btn" type="button" onClick="spl_character('ñ');">ñ</button>
</div>
</form>
<!-- tabbed view for bilingual words -->
<div class="col col-md-8 bi">
<ul class="nav nav-tabs tab-container hide-tabs lang-tabs" role="tablist">
<li class="nav active" id="sp_tab" onClick="$(this).addClass('active'); $('#en_tab').removeClass('active'); $('#dict_eng').addClass('hide-div'); $('#dict_span').removeClass('hide-div');">Spanish</li>
<li class="nav" id="en_tab" onClick="$(this).addClass('active'); $('#sp_tab').removeClass('active'); $('#dict_span').addClass('hide-div'); $('#dict_eng').removeClass('hide-div');">English</li>
</ul>
<div class="dictionary-result" id="meaning">
<p class="box-text">This bilingual dictionary is an actively growing resource accumulating new words each day. Currently drawing from the best names in the world of Spanish/English dictionary, such as <strong>Collins</strong><sup>®</sup> and <strong>Harrap</strong><sup>®</sup>, it continues to improve with every lookup you perform. It includes regionalism, colloquialism, and other non-standard quirkiness from over a dozen Spanish dialects ranging from Peninsular to Mexican and Argentinean to Cuban. This dictionary also includes a growing number of specialty terms specific to niches such as medicine, economics, politics, etc.</p>
<p class="box-text">Please use this page only for dictionary lookups and not comprehensive translations. You can enter either English or Spanish terms and the dictionary will automatically guess the language it belongs to. Keep your inputs to within 20 characters (that should be long enough to handle any English or Spanish word you might want to look up).</p>
</div>
</div>
<!-- sidebar -->
<div class="col col-md-4">
<!-- history panel -->
<div class="panel panel-default panel-box card-effect">
<div class="panel-heading panel-title">Recent Lookups</div>
<div id="recent-lookups" class="panel-body panel-text">
No historical data to show right now. Words will start appearing here as you begin your lookups.
</div>
</div>
<!-- WOTD panel -->
<div class="panel panel-default panel-box card-effect">
<div class="panel-heading panel-title">Word of the Day</div>
<div id="wotd" class="panel-body panel-text">
Word of the day not currently available.
</div>
</div>
</div>
</div>
</div>
Finally I got it to work!! The offending code was in the lookup_word() function:
if(!!(window.history && history.pushState)){ history.pushState(null, null, addr); }
else{ window.location.replace(addr); }
I just removed the if block and replaced it with history.pushState(null, null, addr); window.location.replace(addr);. Don't know why or how but this resolved the problem.
This :
.otherwise({
title: 'TheSite – Radical Spanish learning tips and tricks for the adventurous learner',
templateUrl : 'pages/home.html',
controller : 'mainController'
});
Can be replaced by this :
.otherwise("/");
In your HTML:
This should be avoided :
onclick="$('#word').blur(); lookup_check($('#word').val());"
You may put some JQuery events, but the values can't be passed from JQuery.
It should look something like that :
onclick="$('#word').blur(); lookup_check(variableValueBindToInput)"
Could you show the lookup_check function?
And also show how you make the call to the look_up function from the link?
Here is a Plunker, using your scripts, in the angular way.
http://plnkr.co/edit/EP8y7DrTmzr0WdRkQSew?p=preview
Look here for binding html.
Does
var loc = window.location.href;
var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
if(lastpart != 'dictionary'){ lookup_check(decodeURI(lastpart)); }
Makes the XHR request on page load when the link is clicked (which is setting the request words in the url)?
If this is the case, can't you use:
str = str + "<a ng-click='lookup_checkFromLink(request)'>";
And don't check on page load?
Because, in AngularJs everything inside the app (#) does not reload the whole script when changing the route, which is a core concept of Single Page Applications : not reload all content when only a part of it needs to be changed.
I have a list of Schools displayed in my list.html.twig. For each school I need to insert some data which is filled in a form inside a modal. I need that once the form is filled, the modal is submitted and closes, showing again the background page. Normally the submit action of the modal causes page refresh, and I want to avoid that obviously.
The inspiration for the code was this tutorial, specifically I followed the creation of the form...
//school controller
$school = new School();
$form = $this->createForm(
new SchoolFormType($param),
$school,
array(
'action' => $this->generateUrl("school_modal_vp", array(
'param' => $param,
)),
'method' => 'POST'
));
if($request->isMethod('POST')) {
$form->handleRequest($request);
if($form->isValid()) {
$data = $form->getData();
$em->persist($data);
$em->flush();
$response = new Response(json_encode([
'success' => true,
]));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
... and the function which "replaces" the submit action of the modal with a AJAX call with form data, storing it to database and closing modal.
<script>
var param_id = '{{ param.id }}';
function sendForm(form, callback) {
// Get all form values
var values = {};
$.each( form[0].elements, function(i, field) {
if (field.type != 'checkbox' || (field.type == 'checkbox' && field.checked)) {
values[field.name] = field.value;
}
});
// Post form
console.log(values);
$.ajax({
type : form.attr( 'method' ),
url : form.attr( 'action' ),
data : values,
success : function(result) { callback( result ); }
});
}
$(function() {
$("#school_"+param_id+"_save").on("click", function( e ) {
e.preventDefault();
sendForm($("#myModalSchool_" + param_id).find('form'), function (response) {
$("#myModalSchool_" + param_id).modal('hide');
});
});
});
</script>
However, this works only for the last modal created while listing the schools. Any help is appreciated, and please if you need ask for details.
EDIT 1:
This is the template as requested
<div class="modal fade" data-backdrop="static" id="myModalSchool_{{ param.id }}">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h3 class="modal-title">
School
</h3>
</div>
<div class="modal-body">
<form id="school_{{ param.id }}" name="school_{{ param.id }}" method="post" action="{{ path('school_modal_vp', {param_id: param.id, }) }}" class="form-horizontal">
{{ form_errors(form) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
</div>
</div>
I think the main problem is the var param_id = '{{ param.id }}'; which is defined manually in your javascript.
First, I advise you to add a class on all your save button (e.g modal-submit) and a data-id on each button.
Example:
<button type="button" class="btn btn-primary modal-submit" data-id="{{myData.id}}">Submit</button>
Then in your javascript when you click on a save button (with modal-submit), you retrieve the id from the data-id and execute the sendForm($("#myModalSchool_" + param_id).find('form'),....
Example:
$(function() {
$(".modal-submit").on("click", function( e ) {
e.preventDefault();
var param_id = $(this).attr('data-id');
sendForm($("#myModalSchool_" + param_id).find('form'), function (response) {
$("#myModalSchool_" + param_id).modal('hide');
});
});
});
EDIT:
Saved multiple times issue ?
Moreover, i think you defined the javascript above in each modal. That's why the save is called multiple times. You need to have only one instance of this javascript (so it can't be placed in your modal view). Try to put the javascript in your global layout.
Hope it will help