I have a journal app where I have a fancytree showing the journal entries for the user logged in. Below that tree, I have another fancytree with a dropdown above it that lets you select other users and browse their journal entries. Once you select a user, it grabs their user ID, then loads the second tree with their entries. This works great until about the 7th selection, in which the tree hangs up and basically kills my Tomcat session. I have to restart Tomcat in order to fix it.
My HTML is:
<div class="journalList left-column">
<div id="logTree"></div>
<div class="browser">
<div class="userWrapper">
<h4>Browse Other User Logs</h4>
<select id="otherUser">
</select>
</div>
<div id="browseTree"></div>
</div>
</div>
The ID logTree is the fancytree for the user that is logged in. The browseTree is for the selected user.
Relevant code in JavaScript for #logTree.
$('#logTree').fancytree({
source: {
type: 'post',
cache: false,
url: "/LogbookServlet",
data: {action:"init", sub:"logtree"},
datatype: 'json'
},
lazyLoad: function(event, data) {
var node = data.node;
data.result = getLogTreeData(node.key);
},
loadChildren: function(event, data) {
var node = data.node;
var activeNode = $("#logTree").fancytree("getActiveNode");
if (activeNode) {
activeNode.setExpanded(true);
var mon = new Date().getMonth();
var curMonth = months[mon];
var children = activeNode.getChildren();
if (children) {
for (var i = 0; i < children.length; i++) {
var childNode = children[i];
var title = childNode.title;
if (title == curMonth) {
$("#logTree")
.fancytree("getTree")
.activateKey(childNode.key);
}
}
}
}
}
});
This section of code is called when the page is first loaded:
var otherUsrId;
$('#browseTree').fancytree({
source: [],
lazyLoad: function(event, data) {
var node = data.node;
data.result = getLogTreeData(node.key, otherUsrId);
},
loadChildren: function(event, data) {
var node = data.node;
}
});
I have a handler for the Select to detect the change of users:
$('#otherUser').on('change select', function() {
// Get the User ID from the Select
otherUsrId = $(this).val();
getUserTree(otherUsrId);
});
And here is the code for getUserTree():
function getUserTree(otherUsrId) {
var userTree = $('#browseTree').fancytree("getTree");
userTree.reload({
url: "/LogbookServlet",
data: {action:"init", sub:"logtree", otherUsrId:otherUsrId},
datatype: 'json'
});
}
So, after selecting a user for the 7th time, the "browseTree" fancytree just hangs with the loading icon spinning and it kills my browser. Any ideas? It appears that maybe something is getting called too recursively? Not sure.
I'm not totally sure it will help, but I noticed a critical bit. Please try adding this before creating the tree:
$(":ui-fancytree").fancytree("destroy");
This missing destroy has (sometimes) caused problems for me on reload, so I hope it'll fix your issue, too ;-)
So, I figured out my problem.... Turns out I was doing a recursive call to a function called getChildren() on the server side. Since I'm using lazy loading in the entire second tree, I didn't need to call this.
Related
Using Fullcalendar 4, I am trying to show/hide my resources using a select menu. When the user selects one of the providers from a menu, I want to only show that one resourc's events.
Above my fullcalendar I have my select menu:
<select id="toggle_providers_calendar" class="form-control" >
<option value="1" selected>Screech Powers</option>
<option value="2">Slater</option>
</select>
I am gathering the resources I need using an ajax call on my included fullcalendar.php page. I am storing them in an object and then trying to control which resources are shown onscreen:
document.addEventListener('DOMContentLoaded', function() {
var resourceData = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
console.log(resourceData);
});
//below, set the visible resources to whatever is selected in the menu
//using 1 in order for that to show at start
var visibleResourceIds = ["1"];
//below, get the selected id when the the menu is changed and use that in the toggle resource function
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
var calendar_full = document.getElementById('calendar_full');
var calendar = new FullCalendar.Calendar(calendar_full, {
events: {
url: 'ajax_get_json.php?what=location_appointments'
},
height: 700,
resources: function(fetchInfo, successCallback, failureCallback) {
// below, I am trying to filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
...
});
// below, my toggle_providers_calendar will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
To make sure the getJSON is working, I have console.log(resourceData). The information in the console once it's gathered is:
[{id: '1', title: 'Screech Powers'}, {id: '2', title: 'Slater}]
... the above are the correct resources that can be chosen/rendered. So that seems to be okay.
On page load, no resources show at all, when resource id of '1' (Screech Powers) should be shown per my code. Well, at least, that's what I am trying to do right now.
When the menu changes, resources will show/hide, but not based on what's selected; the logic of only showing what is selected in the menu doesn't seem to be working.
I used to use a URL request for my resources: 'ajax_get_json.php?what=schedule_providers_at_location', and it worked fine! All resources show then their events properly. I am just trying to modify it by using a menu to show/hide the resources as needed.
Here's what I'm doing to make it happen so far! In case someone comes across this post ever, this will help.
Here's my code before my fullcalendar code.
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
My select menu with id 'toggle_providers_calendar' is the same as my original post. My fullcalendar resources as a function is the same too.
After the calendar is rendered, here are the changes I made to my toggle resources function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
This causes the resources to show and hide properly. If the user selects "Show All" that works too!
In order to have a default resource show on load, I add this to my fullcalendar script:
loading: function(bool) {
if (bool) {
//insert code if still loading
$('.loader').show();
} else {
$('.loader').hide();
if (initial_load) {
initial_load = false;
//code here once done loading and initial_load = true
var default_resource_to_show = "<?php echo $default_provider; ?>";
if (default_resource_to_show) {
//set the menu to that provider and trigger the change event to toggleresrource()
$('#toggle_providers_calendar').val(default_provider).change();
} else {
//pass in nothing meaning 'select all' providers for scheduler to see
toggleResource();
}
}
}
},
I am using a bool variable of initial_load to see if the page was just loaded (basically not loading data without a page refresh). The bool of initial_load = true is set outside of DOMContentLoaded
<script>
//show selected date in title box
var initial_load = true;
document.addEventListener('DOMContentLoaded', function() {
My only current problem is that when toggleResource function is called, the all day vertical time block boundaries don't line up with the rest of the scheduler. Once I start navigating, they do, but I don't understand why it looks like this on initial load or when toggleResource() is called:
Any thoughts on how to correct the alignment of the allday vertical blocks?
I have a FullCalendar scheduler on a webapp which has 2 way databinding for resources and events, all working great. I want to be able to present the user with a dropdown that enables them to toggle the visibility of a column, ideally completely client side.
I have tried a combination of addResource / removeResource however my issue here is that a rerender of the calendar (e.g. when a new event is added) then displays the previously removed resource. I can work around this however would prefer a really simple approach using JS / CSS. I currently cannot find a way to set a resource to not be visible, or to have zero width - is this possible?
There is an easy way to do this:
Store resources in an array variable resourceData.
Create another array called visibleResourceIds to store the ids of any resources you want to show.
In the resources callback function, filter resourceData to only contain the resources where the resource id exists in visibleResourceIds. Return the filtered array and fullcalendar will only add the desired resources for you.
To remove a resource from view, simply remove the resource id from visibleResourceIds and refetchResources. To add the resource back in, add the id to visibleResourceIds and refetchResources. DONE.
JSFiddle
var resourceData = [
{id: "1", title: "R1"},
{id: "2", title: "R2"},
{id: "3", title: "R3"}
];
var visibleResourceIds = ["1", "2", "3"];
// Your button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
$('#calendar').fullCalendar('refetchResources');
}
$('#calendar').fullCalendar({
defaultView: 'agendaDay',
resources: function(callback) {
// Filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
callback(filteredResources);
}
});
I had the same challenge. Instead of a dropdown, I use checkboxes, but the workings will be the same.
My resources are stored in a variable, when I uncheck a box, the resource is removed and the resource's object is added to another array with the resourceId as key, and the index added to the object to restore the object in the same column as it originally was. When re-checking the box, the object is added to the resources array and the resources refetched.
/* retrieve the resources from the server */
var planningResources;
var removedResource = [];
$.ajax({
url: '/planning/resources/',
method: 'get',
success: function (response) {
planningResources = response;
showCalendar();
}
, error: function () {
if (typeof console == "object") {
console.log(xhr.status + "," + xhr.responseText + "," + textStatus + "," + error);
}
}
});
/* create the calendar */
showCalendar = function () {
$('#calendar').fullCalendar({
...
});
}
/* checkbox on click */
$('.resource').click(function() {
var resourceId = $(this).val();
var hideResource = !$(this)[0].checked;
$('.status:checkbox:checked').each(function () {
});
if(hideResource) {
$.each(planningResources, function(index, value){
if( value && value.id == resourceId ) {
value.ndx = index;
removedResource[resourceId] = value;
planningResources.splice(index,1);
return false;
}
});
$('#planningoverview').fullCalendar(
'removeResource',
resourceId
);
}
else {
planningResources.splice(removedResource[resourceId].ndx, 0, removedResource[resourceId]);
$('#planningoverview').fullCalendar('refetchResources');
}
});
showCalendar();
It probably doesn't get first price in a beauty contest, but it works for me ...
Cheers
You can use the resourceColumns option for this. In the column objects you can set the width property to a number of pixels or a percentage. If you pass a function here you can easily handle the width property someplace else. Your hide/show function can then set the width to 0 to hide the column. After that you can trigger reinitView to update the view: $('#calendar').fullCalendar("reinitView");
I have been working on a end-to-end test using Webdriver I/O from Jasmine. One specific scenario has been giving me significant challenges.
I have a page with 5 links on it. The number of links actually challenges as the page is dynamic. I want to test the links to see if each links' title matches the title of the page that it links to. Due to the fact that the links are dynamically generated, I cannot just hard code tests for each link. So, I'm trying the following:
it('should match link titles to page titles', function(done) {
client = webdriverio.remote(settings.capabilities).init()
.url('http://www.example.com')
.elements('a').then(function(links) {
var mappings = [];
// For every link store the link title and corresponding page title
var results = [];
for (var i=0; i<links.value.length; i++) {
mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
results.push(client.click(links.value[i])
.getTitle().then(function(title, i) {
mappings[i].pageTitle = title;
});
);
}
// Once all promises have resolved, compared each link title to each corresponding page title
Promise.all(results).then(function() {
for (var i=0; i<mappings.length; i++) {
var mapping = mappings[i];
expect(mapping.linkTitle).toBe(mapping.pageTitle);
}
done();
});
});
;
});
I'm unable to even confirm if I'm getting the link title properly. I believe there is something I entirely misunderstand. I am not even getting each links title property. I'm definately not getting the corresponding page title. I think I'm lost in closure world here. Yet, I'm not sure.
UPDATE - NOV 24
I still have not figured this out. However, i believe it has something to do with the fact that Webdriver I/O uses the Q promise library. I came to this conclusion because the following test works:
it('should match link titles to page titles', function(done) {
var promise = new Promise(function(resolve, reject) {
setTimeout(function() { resolve(); }, 1000);
});
promise.then(function() {
var promises = [];
for (var i=0; i<3; i++) {
promises.push(
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 500);
})
);
}
Promise.all(promises).then(function() {
expect(true).toBe(true)
done();
});
});
However, the following does NOT work:
it('should match link titles to page titles', function(done) {
client = webdriverio.remote(settings.capabilities).init()
.url('http://www.example.com')
.elements('a').then(function(links) {
var mappings = [];
// For every link store the link title and corresponding page title
var results = [];
for (var i=0; i<links.value.length; i++) {
mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
results.push(client.click(links.value[i])
.getTitle().then(function(title, i) {
mappings[i].pageTitle = title;
});
);
}
// Once all promises have resolved, compared each link title to each corresponding page title
Q.all(results).then(function() {
for (var i=0; i<mappings.length; i++) {
var mapping = mappings[i];
expect(mapping.linkTitle).toBe(mapping.pageTitle);
}
done();
});
})
;
});
I'm not getting any exceptions. Yet, the code inside of Q.all does not seem to get executed. I'm not sure what to do here.
Reading the WebdriverIO manual, I feel like there are a few things wrong in your approach:
elements('a') returns WebElement JSON objects (https://code.google.com/p/selenium/wiki/JsonWireProtocol#WebElement_JSON_Object) NOT WebElements, so there is no title property thus linkTitle will always be undefined - http://webdriver.io/api/protocol/elements.html
Also, because it's a WebElement JSON object you cannot use it as client.click(..) input, which expects a selector string not an object - http://webdriver.io/api/action/click.html. To click a WebElement JSON Object client.elementIdClick(ID) instead which takes the ELEMENT property value of the WebElement JSON object.
When a client.elementIdClick is executed, the client will navigate to the page, trying to call client.elementIdClick in the next for loop cycle with next ID will fail, cause there is no such element as you moved away from the page. It will sound something like invalid element cache.....
So, I propose another solution for your task:
Find all elements as you did using elements('a')
Read href and title using client.elementIdAttribute(ID) for each of the elements and store in an object
Go through all of the objects, navigate to each of the href-s using client.url('href'), get the title of the page using .getTitle and compare it with the object.title.
The source I experimented with, not run by Jasmine, but should give an idea:
var client = webdriverio
.remote(options)
.init();
client
.url('https://www.google.com')
.elements('a')
.then(function (elements) {
var promises = [];
for (var i = 0; i < elements.value.length; i++) {
var elementId = elements.value[i].ELEMENT;
promises.push(
client
.elementIdAttribute(elementId, 'href')
.then(function (attributeRes) {
return client
.elementIdAttribute(elementId, 'title')
.then(function (titleRes) {
return {href: attributeRes.value, title: titleRes.value};
});
})
);
}
return Q
.all(promises)
.then(function (results) {
console.log(arguments);
var promises = [];
results.forEach(function (result) {
promises.push(
client
.url(result.href)
.getTitle()
.then(function (title) {
console.log('Title of ', result.href, 'is', title, 'but expected', result.title);
})
);
});
return Q.all(promises);
});
})
.then(function () {
client.end();
});
NOTE:
This fails to solve your problem, when the links trigger navigation with JavaScript event handlers not the href attributes.
I probably misunderstood something but here is my problem on plunker.
I put the relevant code here anyway:
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
var cellNameEditable =
'<cell-template model=COL_FIELD input=COL_FIELD entity=row.entity></cell-template>';
var cellNameDisplay =
'<div class="ngCellText" ng-class="col.colIndex()">{{row.getProperty(col.field)}}</div>';
$scope.myData= [{"id":1,"code":"1","name":"Ain"},{"id":2,"code":"2","name":"Aisne"},{"id":3,"code":"3","name":"Allier"},{"id":4,"code":"5","name":"Hautes-Alpes"},{"id":5,"code":"4","name":"Alpes-de-Haute-Provence"},{"id":6,"code":"6","name":"Alpes-Maritimes"},{"id":7,"code":"7","name":"Ardèche"},{"id":8,"code":"8","name":"Ardennes"},{"id":9,"code":"9","name":"Ariège"},{"id":10,"code":"10","name":"Aube"}];
$scope.gridOptions = {
data: 'myData',
multiSelect: false,
enableCellSelection: true,
enableRowSelection: false,
enableCellEditOnFocus: false,
rowHeight: 100,
columnDefs: [
{field:'id', displayName:'Id', visible: false},
{field:'code', displayName:'Code', enableCellEdit:true},
{
field:'name', displayName:'Name', enableCellEdit:true,
cellTemplate: cellNameDisplay,
editableCellTemplate: cellNameEditable
}
]
};
});
app.directive('cellTemplate', function () {
var cellTemplate =
'<div><form name="myForm" class="simple-form" novalidate>' +
'<input type="text" name="myField" ng-input="localInput" ng-model="localModel" entity="entity" required/>' +
'<span ng-show="myForm.myField.$error.required"> REQUIRED</span>' +
'localModel = {{localModel}} localInput = {{localInput}} entity = {{entity}}' +
'</form></div>';
return {
template: cellTemplate,
restrict: 'E',
scope: {
localModel:'=model',
localInput:'=input',
entity:'=entity'
},
controller: function ($scope) {
$scope.$on('ngGridEventStartCellEdit', function (event) {
console.log('cellTemplate controller - ngGridEventStartCellEdit fired');
$scope.oldEntity = angular.copy(event.currentScope.entity);
$scope.oldValue = angular.copy(event.currentScope.localModel);
});
$scope.$on('ngGridEventEndCellEdit', function(event) {
console.log('ngGridEventEndCellEdit fired');
if(event.currentScope.myForm.$valid) {
if(!angular.equals($scope.oldEntity, event.currentScope.entity)) {
alert('data saved !');
}
} else {
$scope.localModel = angular.copy($scope.oldValue);
$scope.localInput = angular.copy($scope.oldValue);
$scope.entity = angular.copy($scope.oldEntity);
}
});
}
};
});
Then explanations:
I have a ng-grid and based on the official example named "Excel-like Editing
Example" but with enableCellEditOnFocus option turned to false.
The cell "name" is defined in a directive containing a form to handle
data validation before updating the model.
I want to implement this behavior: When a user put invalid data, the
directive display error message and when the user leave the field, the
directive rollback data. If everything ok then I let the data updated.
The rollback part does not work. On the given plunker line 67 to 72 (last block on the code given here) it
fails to retore data. But my binding is with "=" so it should. Or maybe
because I am on the ngGridEventEndCellEdit event it breaks the links ?
I really don't understand why it fail.
So to reproduce my issue: enter in modification on a name cell, delete
all the data, REQUIRED is shown, then go out from the cell -> model is
not rolled back.
If you use a custom template, you should emit ngGridEventEndCellEdit event.
I'm new to Meteor.
Trying to render items from collection but Meteor.renderList(observable, docFunc, [elseFunc]) alway go to elseFunc.
this.ComponentViewOrdersFlow = Backbone.View.extend({
template: null,
initialize: function() {
var frag;
Template.ordersFlow.events = {
"click a": function(e) {
return App.router.aReplace(e);
}
};
this.template = Meteor.render(function() {
return Template.ordersFlow();
});
console.log(Colors);
frag = Meteor.renderList(
Colors.find(),
function(color) {
console.log(color);
},
function() {
console.log('else consdition');
}
);
},
render: function() {
this.$el.html(this.template);
return this;
}
});
Initially I thought that Collection is empty, but console.log(Colors) shows that there are items in collection. Moreover if I use Meteor.render(... -> Template.colors({colors: Colors.find()}) ) it renders template end show Collection items there.
Meteor version 0.6.6.3 (Windows 7, 64bit)
Mongo - connected to MongoLab
Thank you for any help.
Jev.
Can't really explain this well in the comments, so here is a very, very simple example of using the Meteor template engine. This is a 100% functional app, showcasing basic reactivity. Note that I never call render() or renderList() anywhere.
All this app does is show a button, that when clicked, adds a number to a list. The number is reactively added to the list, even though I never do anything to make that reactivity explicit. Meteor's templates are automatically reactive! Try it out - this is all of the code.
numbers.html:
<body>
{{> numberList}}
</body>
<template name="numberList">
<ul>
{{#each numbers}}
<li>{{number}}</li>
{{/each}}
</ul>
<button>Click Me</button>
</template>
numbers.js:
var Numbers = new Meteor.Collection("numbers");
if (Meteor.isClient) {
Template.numberList.numbers = function() {
return Numbers.find();
};
var idx = 0;
Template.numberList.events({
"click button": function() {
Numbers.insert({
number: idx
});
idx++;
}
});
}