Rendering gridstack in Meteor - javascript

I am using gridstack.js (https://github.com/troolee/gridstack.js) in Meteor.
I have the grid
<template name="grid">
<div class="grid-stack" data-gs-width="12" data-gs-animate="yes">
{{#each tiles}}
{{> gridItem}}
{{/each}}
</div>
</template>
and I apply gridstack to my grid element with
Template.grid.onRendered(function() {
$('.grid-stack').gridstack();
});
It works as it should, but if I first go to another route and then back to the route with the grid, the gridstack features are not "active" any longer (but the console doesn't say anything wrong). If I refresh, the gridstack will again work as it should. So the problem occurs only when I come back to this page without refreshing the entire page.
I have tried changing onRendered to onCreated, and suddently the grid gets the exact same behaviour when I refresh, i.e. the gridstack doesn't work neither when refreshing the page nor when I first go to another page and then back to the page with the grid.
So I guess I should still use onRendered, but it seems Meteor doesn't render the same way when I 'browse' between pages. Should I probably deinitialize the library when I leave the page, so it can initialize correctly again when the template is rerendered?
Edit
I have tried
Template.grid.onRendered(function() {
var $el = $('.grid-stack');
// destroy if already applied
if ($el.data('gridstack')) {
$el.data('gridstack').destroy();
}
// apply gridstak to grid element
var grid = $el.gridstack();
});
and also
Template.grid.onDestroyed(function() {
$('.grid-stack').data('gridstack').destroy();
});
but neither works.
But it says
TypeError: $(...).data(...).destroy is not a function

I've faced the same problem while I was implementing Gridstack on AngularJS. The Page change breaks the Gridstack. Here is how I solved the issue in my controller:
.controller('HomeCtrl', ['$scope', 'analyticsService', '$modal', '$timeout', function($scope, analyticsService, $modal, $timeout) {
$scope.homeWidgets = [
{sizeX: 6, sizeY: 5, row: 0, col:0, type: 'ndl-action-items'},
{sizeX: 6, sizeY: 5, row: 0, col:7, type: 'ndl-stage-shift-graph'},
{sizeX: 6, sizeY: 5, row: 1, col:0, type: 'ndl-nodules-in-system'},
{sizeX: 6, sizeY: 5, row: 1, col:7, type: 'ndl-case-volume'}
];
$scope.loadGrid = function() {
if ($scope.homeGrid && $scope.homeGrid.destroy) {
$scope.homeGrid.destroy();
}
var options = {
cell_height: 70,
vertical_margin: 10
};
// console.log("init grid");
$scope.homeGrid = $('.grid-stack').gridstack(options);
// console.log($scope.homeGrid);
};
$scope.$on('$viewContentLoaded', function(event) {
$timeout($scope.loadGrid, 300);
// event.preventDefault();
/* Act on the event */
// console.log('loaded');
// $scope.loadGrid();
});
}]);
So basically when my view reloads, I destroy the grid object if it exists and then recreate it. I had to use timeout so that the DOM gets loaded before the grid is initialized.

Related

Angular ui grid does not show content unless browser window is resized

I am using angularjs 1.5.0 with angular ui grid 3.1.1.
When I assign gridOptions (passed to the grid directive) object in controller body like this:
$scope.gridOptions = {
data: [{"mock2": 1, "mock1": 2}, {"mock2": 10, "mock1": 22} ]
};
HTML:
<div ui-grid="gridOptions"></div>
It displays the table as expected. But when I try to change data inside $scope.on:
$scope.$on('update', function (event, passedFromBroadcast) {
$scope.gridOptions.data= [{"mock2": "set", "mock1": "inside"}, {"mock2": "$scope", "mock1": "on"} ] ;
});
It renders only frame of the table, when including pagination it will also render pagination related controls - but not the content itself.
Content (the rows and column headers) appear only after I resize my browser window.
Why doesn't angular-ui grid update table content when the data changes inside $scope.on?
How can I manually update the angular ui grid table?
Things I already tried:
$scope.gridApi.core.handleWindowResize(); // Does not work
$scope.gridApi.core.refresh(); // Does not work
$timeout(function(){$scope.gridOptions.data= [{"mock2": "set", "mock1": "inside"}, {"mock2": "$scope", "mock1": "on"} ] ;}) // Does not work
It's a known bug when the gridOptions.data length doesn't change after update, proposed solution is to clear data and with use of $timeout refresh it
$scope.$on('update', function (event, passedFromBroadcast) {
$scope.gridOptions.data = null
$timeout(function(){
$scope.gridOptions.data = [{"mock2": "set", "mock1": "inside"}, {"mock2": "$scope", "mock1": "on"} ] ;
});
});
There are a two issues with this code, but the reason for the table contents showing only after browser window resize is lack of css class defining width and height, basic (working) example:
.grid {
width: 500px;
height: 250px;
}
and in HTML:
<div class="grid" ui-grid="gridOptions"></div>
The Other issue (mentioned by other people in this thread:
assigning gridOption fields (data but also columnDefs) must be done inside $timeout, additionally both data and columnDefs need to be cleared before that. Otherwise it change might not become visible and table contents and headers will remain unchanged (known bug in ui-grid as #maurycy mentioned)
Putting the window resize handler on $interval has worked for me in the past inside the gridOptions.onRegisterApi method:
var gridResizeHandlerInterval;
$scope.gridOptions = {
..., // etc.
onRegisterApi: function(gridApi) {
ctrl.gridApi = gridApi;
gridResizeHandlerInterval = $interval( function() {
$scope.gridApi.core.handleWindowResize();
}, 10, 500);
}
};
And then make sure you tidy up and cancel the interval when your controller gets destroyed:
$scope.$on('$destroy', function() {
$interval.cancel(gridResizeHandlerInterval);
});
Hope this helps you...
You can try the below:
$scope.$on('update', function (event, passedFromBroadcast) {
$scope.gridOptions.data.length = 0;
$timeout(function(){
$scope.gridOptions.data = [{"mock2": "set", "mock1": "inside"}, {"mock2": "$scope", "mock1": "on"} ] ;
});
});
Hope this helps!

Angular custom directives not working with external javascript plugin

I am creating a custom angular directive that will use the Image Tilt Effect plugin.
<tilt></tilt>
the template looks like this.
<li class="grid__item">
<div class="grid__img grid__img--example-1" >
<img src="img/3.jpg" class="tilt-effect" alt="grid01" data-tilt-options='{ "opacity" : 0.3, "extraImgs" : 3, "movement": { "perspective" : 1200, "translateX" : -5, "translateY" : -5, "rotateX" : -5, "rotateY" : -5 } }' />
</div>
The problem I have is this script that I am loading at the bottom of the page doesnt seem to be on when the custom directive is injected into the page.
<script src="js/tiltfx.js"></script>
If I try and move the script into the html fragment for the custom directive I get an Error: $compile:tplrt
I've created a wrapper directive for this the Image Tilt Effect plugin (fiddle).
When you have a DOM plugin you need to use in angular, don't use auto initialization, such as the data-tilt-options of this plugin, because they are hard to predict, and may cause weird behavior, memory leaks, etc... This plugin has a manual init method - new TiltFx(element, options), so we can use that.
Another problem is that this plugin must wait for angular to finish rendering, before it should be initialized. The simple fix is just using setTimeout or $timeout (if we need to update the digest cycle), to put the init at the end of the JS queue. We can also use mutation observers to do that, but that's out of this answer's scope.
One troubling aspect of using a DOM plugin in angular, is memory leaks. Plugins should have some sort of a cleaning mechanism. This plugin doesn't. So, you'll have to check for memory leaks, and if there are stray event handlers remove them, when the wrapped element is removed.
Directive code:
appModule.directive('tilt', function tilt() {
var ddo = {
template: '<div class="grid__img"><img class="tilt-effect" ng-src="{{imgSrc}}" alt="The image" />',
restrict: 'E',
replace: true,
scope: {
imgSrc: '#', // the image src
tiltOptions: '=?' // the tilt options object - optional
},
link: function (scope, $el) {
var img = $el[0].querySelector('img.tilt-effect'); // find the img element
var tilt;
setTimeout(function () { // wait 'till directive is rendered
tilt = new TiltFx(img, scope.tiltOptions); // apply tilt on image with options (if any)
});
$el.on('$destroy', function() {
// use tilt variable to perform cleanup on wrapper and img if needed
tilt = null;
});
}
};
return ddo;
});
Usage:
<div ng-app="tilt">
<tilt img-src="http://cdn.cutestpaw.com/wp-content/uploads/2013/12/Most-Famous-Felines-034.jpg" tilt-options='{ "opacity" : 0.3, "extraImgs" : 3, "movement": { "perspective" : 1500, "translateX" : -5, "translateY" : -5, "rotateX" : -5, "rotateY" : -5 } }'></tilt>
<tilt img-src="http://www.cats.org.uk/uploads/images/pages/photo_latest14.jpg"></tilt>
</div>
Don't forget that this plugin requires the container to have fixed width and height, for example:
.grid__img {
width: 400px;
height: 400px;
}
Change
<script src="js/tiltfx.js"></script>
to
<script ng-src="js/tiltfx.js"></script>

how to use angular-gridster and highcharts-ng directives together in angularjs

I am using angularjs-gridster (https://github.com/ManifestWebDesign/angular-gridster) with higharts-ng directive (https://github.com/pablojim/highcharts-ng/blob/master/README.md)
I am trying to generate these highcharts inside the grid cells. My problem is that the highcharts are occupying their default width and height (600px * 400px) even when i place my graph drawer function in a $timeout service. Here's the code:
HTML:
<div class="graph-list" gridster="gridsterOpts">
<ul>
<li gridster-item="graph.grid" class="graph-set" ng-repeat="graph in graphs | orderBy: 'number'">
<highchart id="{{'graph' + graph.number}}" class="graph" config="graph.config"></highchart>
</li>
</ul>
</div>
JS:
// inside the graph-list div controller
$scope.gridsterOpts = {
colums: 4,
rowHeight: 240,
margins: [10,10],
outerMargin: false,
draggable: {
enabled: false // whether dragging items is supported
}
};
$scope.graphs = {}; //
$scope.somefunction(){ /* this function populates the graphs object */ };
function drawGraphs(){ /* this function populates the graph.config object by looping through all the graph objects */ }
$timeout(function(){
drawGraphs();
});
I have tried creating watch on the grid-cell width and height but it shows no change. I have not given the highchart width and height explicitly in the graph.config options because I read in the highcharts-ng documentation that it takes the parent width and height by default but its not happening. Can anyone guide me what could be the possible problem.
Seems to me that the angularjs-gridster plugin is not able to set the grid width and height before the highcharts directive is able to render itself. Please help.
I eventually did it. I needed to add the chart.reflow() method (which just resizes the chart instead of redrawing it so better performance wise also, I guess) in the func() options as provided in the highcharts-ng documentation.
graph.config = {
options: { },
series: [],
func: function (chart) {
$timeout(function(){
chart.reflow();
})
}
}
Hope it helps someone else.

PhotoSwipe use image to open gallery

Im looking for a solution too open the PhotoSwipe gallery with a
img link. So there is a IMG with a gallery icon. And i want if the
user click on it that the gallery open.
Have someone an idea how i can handel that?
I found this out. But this open on load the gallery.
<script type="text/javascript">
(function(window, PhotoSwipe){
document.addEventListener('DOMContentLoaded', function(){
var
options = {
preventHide: true
},
instance = PhotoSwipe.attach( window.document.querySelectorAll('#Gallery a'), options );
instance.show(0);
}, false);
}(window, window.Code.PhotoSwipe));
</script>
Best regargs
I just started working with photoSwipe so I am not positive this will work but it seems to me you only have to call instance.show(0) on a click event.
Assuming I have this element on the page: <a id="launch-gallery" href="#">Click to launch gallery</a> I could add this jQuery click event to launch the gallery:
$('#launch-gallery').click(function(evt){
evt.preventDefault(); // prevent regular click action
instance.show(0); // Make sure 'instance' variable is available to this function
});
If you are not using jQuery, you can do the same thing in native JavaScript (but a little more verbose).
I hope this helps.
Note that I use php (ajax) to deliver the image locations and sizes, so you'll still have to define the json data yourself.
This is how I did it with Jquery:
$('.element').off(); //in case it's a dynamically changing element
$('.element').on("click tap", function () {
var dataForPhpScript = $(this).parents('.gallery').attr("data-attr"); //data for php script
$.getJSON('urlToPHPFunction?dataID=' + dataForPhpScript, function (json) {
openPhotoSwipe(json);
});
});
And here is the photoswipe opening function:
function openPhotoSwipe(jsonData) {
var pswpElement = document.querySelectorAll('.pswp')[0];
// define options (if needed)
var options = {
// history & focus options are disabled on CodePen
history: false,
focus: false,
showAnimationDuration: 0,
hideAnimationDuration: 0
};
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, jsonData, options);
gallery.init();
}
note that jsonData is supposed to look somewhat like this:
[
{
src: 'https://placekitten.com/600/400',
w: 600,
h: 400
},
{
src: 'https://placekitten.com/1200/900',
w: 1200,
h: 900
}
];
I realise this answer is late, but since this came on top while just googling something entirely different (but photoswipe related), I thought maybe this would be useful!

Changing a Highcharts theme (partially working)

I have a problem with changing the theme for highcharts. I have created an array to hold all the themes and am trying to change them via a select list onChange event.
var highcharts_theme = [];
/* Default theme */
highcharts_theme.push({});
/* Dark Blue theme */
highcharts_theme.push({
colors: ["#DDDF0D", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
chart: {
backgroundColor: {
linearGradient: [0, 0, 250, 500],
stops: [
[0, 'rgb(48, 48, 96)'],
[1, 'rgb(0, 0, 0)']
]
},
.... Shortened for brevity.....
My code to change the theme is :
$('#theme-type').selectmenu({ width: 200 }).change(function (e) {
var themeIndex = parseInt($('#theme-type').val());
Highcharts.theme = highcharts_theme[themeIndex];
// Apply the theme
highchartsOptions = Highcharts.setOptions(Highcharts.theme);
});
The problem I am having is that if for example I switch to the Skies theme it is fine, but then changing to any other theme, the skies background remains along with other elements of the theme.
Does anyone know of a proper way to reset the theme entirely?
Thanks
Every time you set a theme, it merges with the existing theme, and hence any option that is not present in the new theme it will pick from the existing theme. This may not be desired always.
Unfortunately, Highcharts does not give an option to go back to the very defaults that are set at the time of first load. The following code can be used to get that functionality
// Make a copy of the defaults, call this line before any other setOptions call
var HCDefaults = $.extend(true, {}, Highcharts.getOptions(), {});
function ResetOptions() {
// Fortunately, Highcharts returns the reference to defaultOptions itself
// We can manipulate this and delete all the properties
var defaultOptions = Highcharts.getOptions();
for (var prop in defaultOptions) {
if (typeof defaultOptions[prop] !== 'function') delete defaultOptions[prop];
}
// Fall back to the defaults that we captured initially, this resets the theme
Highcharts.setOptions(HCDefaults);
}
After resetting the theme, applying a new theme would work as if that's the first theme being applied.
Demo # jsFiddle
If you remove all of the color options and reload the Highcharts object it will default back to the default basic theme. If you set the below to null it should not show a back ground image once reloaded.
plotBackgroundImage: null

Categories