Is it possible to set the background color for a row in slickgrid (based on data values) AND use pagination? For angular-slickgrid package.
I used getItemMetadata as suggested multiple other (old) posts - example SlickGrid: How to loop through each row and set color based on the condition?.
This code:
metadata(old_metadata_provider) {
return function(row) {
var item = this.getItem(row);
var ret = (old_metadata_provider(row) || {});
if (item) {
ret.cssClasses = (ret.cssClasses || '');
if ("attribute" in item) {
return { cssClasses: 'redRow' }; //redRow is in styles.css
}
}
return ret;
}
}
and the call is:
this.dataViewObj.getItemMetadata = this.metadata(this.dataViewObj.getItemMetadata);
It works correctly. However, when I turn pagination on, the color does not work as expected. I read that SlickGrid re-uses the same row elements when scrolling or paginating and will overwrite the styles associated with them. Is there another way to do it? Thanks for any help on this.
I tried adding the following code, after reading suggestion from ghiscoding, but the colors are still not working when pagination is enabled.
angularGridReady(angularGrid: AngularGridInstance) {
this.angularGrid = angularGrid;
this.dataViewObj = angularGrid.dataView;
this.gridObj = angularGrid.slickGrid;
this.checkRowBackgroundColor(); //where I call the metadata function from my previous post, if the dataView object is defined.
//I added this code:
var self = this;
this.dataViewObj.onPagingInfoChanged.subscribe(function (e, dataView, grid) {
self.gridObj.invalidate();
self.gridObj.render();
});
}
Try this approach:
angularGridReady(angularGrid: AngularGridInstance) {
this.angularGrid = angularGrid;
this.dataViewObj = angularGrid.dataView;
this.gridObj = angularGrid.slickGrid;
// check color change logic for the first time page load
this.checkRowBackgroundColor();
// use arrow function so that 'this' works properly
this.dataViewObj.onPagingInfoChanged.subscribe((e, dataView, grid) => {
// check your color change logic every time Paging Info Changed
this.checkRowBackgroundColor();
});
}
Inside your checkRowBackgroundColor:
checkRowBackgroundColor() {
// ... any of your logic
// get metadata
this.dataViewObj.getItemMetadata = this.metadata(this.dataViewObj.getItemMetadata);
// rerender the grid after getting new metadata
this.gridObj.invalidate();
this.gridObj.render();
}
Your problem should be solved now. As I have not tested on my local machine, I can not give guarantee. You can checkout original documentation of angular-slickgrid about this specific topic here: Dynamically Add CSS Classes to Item Rows
I have a use-case where I need to programmatically add/remove the onClick event associated with a panel.
I have tried the following solution but receive a cijCell.addEventListener is not a function error.
function cij_enabled(){
var cijCell = app.pages.Home.descendants.cellFour;
var index = cijCell.styles.indexOf('disabled-card');
if (Report.riskOfLoss === 'High') {
cijCell.styles.splice(index, 1);
cijCell.addEventListener("click", function() {
app.popups.Customer.visible = true;
});
} else {
if (index === -1){
cijCell.styles.push('disabled-card');
cijCell.removeEventListener("click", function() {
app.popups.Customer.visible = true;
});
}
}
}
How can I achieve the desired outcome? Is adding eventlisteners possible in this fashion through app maker?
You can definitely do so and you got it almost right. The only thing you need to understand is that the appmaker widget is not a native html element hence the error:
cijCell.addEventListener is not a function
Fortunately, AppMaker has a way of getting the native html elements associated to a widget. You need to use the getElement() method and then you can use the add/remove event listeners methods. So you should change your code from cijCell.addEventListener... to cijCell.getElement().addEventListener...
Reference: https://developers.google.com/appmaker/scripting/api/widgets#Panel
I'm using the amazing flatpickr on a project and need the calendar date to be mandatory.
I'm trying to have all the validation in native HTML, so I was naively trying with just adding the required attribute to the input tag, but that doesn't appear to be working.
Is there a way of natively making a date mandatory with flatpickr or do I need to write some custom checks?
You can easily achieve this by:
Passing allowInput:true in flatpickr config.
As example:
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
};
From the documentation:
Allows the user to enter a date directly into the input field. By
default, direct entry is disabled.
The downside of this solution is that you should enable the direct entry (but ideally form validation should occur whether or not direct entry is enabled).
But if you don't want to enable the direct entry to solve this problem, you can use the code below as a workaround:
flatpickrConfig = {
allowInput:true,
onOpen: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', true);
},
onClose: function(selectedDates, dateStr, instance) {
$(instance.altInput).prop('readonly', false);
$(instance.altInput).blur();
}
};
This code remove the readonly property when it is not in focus so that html validation can occur and add back the readonly prop when it is in focus to prevent manual input. More details about it here.
This is what I came up with to make as complete of a solution as possible. It prevents form submission (when no date selected and input is required), ensures browser native "field required" message pops up and prevents the user typing in the value directly.
flatpickrConfig = {
allowInput: true, // prevent "readonly" prop
onReady: function(selectedDates, dateStr, instance) {
let el = instance.element;
function preventInput(event) {
event.preventDefault();
return false;
};
el.onkeypress = el.onkeydown = el.onkeyup = preventInput; // disable key events
el.onpaste = preventInput; // disable pasting using mouse context menu
el.style.caretColor = 'transparent'; // hide blinking cursor
el.style.cursor = 'pointer'; // override cursor hover type text
el.style.color = '#585858'; // prevent text color change on focus
el.style.backgroundColor = '#f7f7f7'; // prevent bg color change on focus
},
};
There is one disadvantage to this: Keyboard shortcuts are disabled when the flatpickr is open (when the input has focus). This includes F5, Ctrl + r, Ctrl + v, etc. but excludes Ctrl + w in Chromium 88 on Linux for some reason. I developed this using a rather old flatpickr version 3.1.5, but I think it should work on more recent ones too.
In case you want to use altFormat (display one date format to user, send other date format to server), which also implies setting altInput: true, you have to also change the onReady function to use instance.altInput instead of instance.element.
The onReady event listener can probably be attached to the instance after initializing it. However, my intention of using flatpickr with vue-flatpickr-component where you cannot elegantly access the individual flatpickr instances, made me use the config field instead.
I haven't tested it on mobile devices.
After digging a bit into the GitHub repo, I found a closed issue that points out that the issue will not be addressed.
In the same Issue page there is a workaround that seems to do the trick:
$('.flatpickr-input:visible').on('focus', function () {
$(this).blur()
})
$('.flatpickr-input:visible').prop('readonly', false)
copy attr name from prior input type hidden to rendered flatpickr input
just do this
$('[name=date_open]').next('input').attr("name","date_open");
$('[name=date_close]').next('input').attr("name","date_close");
Have been working on this for a couple of days now, finally getting the result I was after.
NOTE: I am using flatpickr with jQuery validation
As you would know flatpickr uses an alternative field for the date input, the actual field where the date is stored is hidden, and this is the key.
jQuery validation has a set of defaults, and by default hidden fields are not subject to validation, which normally makes perfect sense. So we just have to turn on the validation of hidden fields to make this work.
$.validator.setDefaults({
ignore: []
});
So my validator rules are then fairly normal:
var valid = {
rules: { dateyearlevel: {required: true} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
That should allow you to ensure the date is required.
In my situation I wanted my date to only be required is a checkbox was checked. To do this we changed the rule above:
var valid = {
rules: { dateyearlevel: {
required: function() { return $("#mycheckbox").is(":checked") }
} },
messages: { dateyearlevel: {required: "The date is required"} }
};
$("#myform").validate(valid);
In case this helps someone, I'm using parsley.js for frontend validation and it works good with flatpickr
enter image description here
Just to expand a bit more on this, I found the ignore value set as an empty array did the trick for me also. You can just add this to your validate call back. Also displaying was a bit of an issue so I updated the errorPlacement to allow for flatpickr inputs like so.
$('#my-form').validate({
errorPlacement: function (error, element) {
if (element.hasClass('js-flatpickr') && element.next('.js-flatpickr').length) {
error.insertAfter(element.next('.js-flatpickr'));
} else if (element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
},
ignore: [],
rules: {
'startdate': { required: true }
},
messages: {
'startdate': {required: "Start Date is required"}
},
submitHandler: function(form) {
// ajax form post
}
});
in my case vue ( dunno why ) , i would like to comment for comment by #mik13ST
fyi: the default allowInput i think is true, no need to define, i didnt set the properties and my flat-pickr also work on testing.
i use
// this work in flat-pickr || #code_01
<small class="text-danger">
{{ validationContext.errors[0] }}
</small>
instead of
// work for all element except <flat-pickr #code_02 , dunno why not work
<b-form-invalid-feedback>
{{ validationContext.errors[0] }}
</b-form-invalid-feedback>
full code
<validation-provider
#default="validationContext"
name="Waktu Selesai Berkegiatan *"
vid="Waktu Selesai Berkegiatan *"
rules="required"
>
<flat-pickr
id="Waktu Selesai Berkegiatan *"
v-model="item.pip_time_end_rl"
placeholder="Waktu Selesai Berkegiatan *"
class="form-control"
static="true"
:config="dpconfig"
:state="getValidationState(validationContext)"
/>
// put here the message of error ( required ) #code_01 instead of #code_02
</validation-provider>
if younot use composite,
just use
#default="{ errors }" // in validation provider
:state="errors.length > 0 ? false : null" // in element for example flat-pickr
{{ errors[0] }} // to print out the message
I'm currently rendering bootstrap modals on my webpage using MeteorJS's "renderWithData" method to load each template when it's needed.
I'm running into an issue where my helper methods which access the data in the modal using "Blaze.getData()" will occasionally return undefined and I'm unsure how to fix that.
The only way I've been able to replicate the issue is by constantly creating/destroying the modals and there doesn't seem to be anything that specifically causes the issue.
Here are the steps I've been taking:
1) I instantiate the modal with the proper data
Template.Courses.events({
'click .share-course': function (e,t) {
var courseID = $(e.target).data('courseid');
Template.instance().activeCourse.set(
createModalWithData(
{
currentInstance: Template.instance().activeCourse.get(),
template: Template.Enrollment_Generator,
dataToRender: {courseID: courseID}
}
));
$('#generateEnrollmentURL').modal('show');
}
});
Also, here is the code for "createModalWithData":
// Create a modal with a specific data context
// If modal template already exists, destroy
// and re-create with the new data context.
// If a location to render isn't specified, renders
// content in the body .
// Parameters: [Object] data { currentInstance : Template || null,
// template : Template,
// dataToRender : Object,
// (optional) location : Element
// Return: Blaze Template Instance
createModalWithData = function createModalWithData(data) {
// Ensure data exists
if (_.isUndefined(data) || _.isNull(data)) {
throw "data cannot be null or undefined";
}
// If modal already exists, destroy it
if (!_.isNull(data.currentInstance)) {
Blaze.remove(data.currentInstance);
}
// If location is undefined, set to page body
if (_.isUndefined(data.location)) {
data.location = document.body;
}
// Render modal with dataToRender
return Blaze.renderWithData(data.template,
data.dataToRender,
data.location
);
};
2) I attempt to retrieve the data using "Blaze.getData()" within my modal template
Template.Enrollment_Generator.onCreated(function() {
var courseID = Blaze.getData().courseID; // Occasionally undefined
Meteor.subscribe('enrollment-codes',courseID);
});
So far I've attempted to replace the "onCreated" method with "onRendered" but still had the same issue.
It turns out the issue was within the click event. I had a nested span element within my share-course button:
<small class="share-course" data-courseid="{{_id}}">
Share
<span class="glyphicon glyphicon-share"></span>
</small>
This was messing up the way I was targeting my embedded courseID
Instead of Blaze.getData(), I should have also been using Template.currentData() to retrieve the data within my template
As stated here: https://forums.meteor.com/t/blaze-getdata-question/5688
I'm using the Vitrux theme in Wordpress that uses Isotope jQuery plugin to display a work porfolio. Isotope allows categories to be used to sort the items, but within the theme it's only possible to sort by one category at a time (e.g. 'Year' or 'Type', not 'Year' and 'Type'.
You can see a mock-up here: http://snaprockandpop.samcampsall.co.uk/shoots/
The jQuery attached to each category item, that sorts the posts, is as follows:
function (){
var selector = $(this).attr('data-filter');
$container_isotope.isotope({ filter: selector });
var $parent = $(this).parents(".filter_list");
$parent.find(".active").removeClass('active');
$(".filter_list").not($parent).find("li").removeClass('active').first().addClass("active");
$(this).parent().addClass("active");
return false;
}
I can see from the Isotope site that it's possible to use multiple filters, and I've found the authors notes on this here: http://jsfiddle.net/desandro/pJ6W8/31/
EDIT:
Editing the theme files has allowed me to assign appropriate classes and properties to the filter lists (you can see these in the page source) and I'm targeting them through an edited version of the jsfiddle to reflect the classes and id's in the theme styling:
$( function() {
var $container = $('#portfolio_container');
$container.isotope({
animationOptions: { duration: 300, easing: 'linear', queue: false },
getSortData : {
year : function ( $elem ) { return parseFloat( $elem.find('._year').text() ); },
live-shows : function ( $elem ) { return parseFloat( $elem.find('._live-shows').text() ); }
}
});
var filters = {};
$('.ql_filter a').click(function(){
var $this = $(this);
if ( $this.hasClass('selected') ) {
return;
}
var $optionSet = $this.parents('.filter_list');
$optionSet.find('.active').removeClass('active');
$this.addClass('active');
var group = $optionSet.attr('data-filter-group');
filters[ group ] = $this.attr('data-filter');
var isoFilters = [];
for ( var prop in filters ) {
isoFilters.push( filters[ prop ] )
}
var selector = isoFilters.join('');
$container.isotope({ filter: selector });
return false;
});
});
Two (fairly major) things:
1) I'm not 100% that I've edited this correctly. Despite Rich's excellent comments I'm still out of my depth. I'm particularly not clear on how to set-up the 'getSortData' section - I think it's right but any input would be great.
2) I'm not sure that this JavaScript is being initiated. At the moment I've placed it immediately before the closing head tag but a check on the page suggests that the original script outlined above is the one running on the filter items.
Any pointers at this stage would be fantastic!
I see what you mean. You are looking for the intersection of both filters and not the mutually exclusive filter values.
Short answer: Contact the theme vendor and see if they can make the intersection filters for you.
Longer assistance (not an answer):
Your ultimate goal is to get the Vitrux theme working the way you want.
Your first goal is to understand what the jsfiddle code is doing.
I can handle your first goal by explicating the code.
// hook into the JQuery Document Load event and run an anonymous function
$( function() {
// Create a variable called container
// make container refer to the element with ID Container
var $container = $('#container');
// initialize isotope
// Call the isotope method on the container element
$container.isotope({
// options...
//distracting options
animationOptions: { duration: 300, easing: 'linear', queue: false },
getSortData : {
price : function ( $elem ) { return parseFloat( $elem.find('.price').text() ); },
size : function ( $elem ) { return parseFloat( $elem.find('.size').text() ); }
}
});
// sorting button
//for the anchor tag that has a class of 'pricelow', wire up an anonymous function to the click event
$('a.pricelow').click(function(){
//Rerun the isotope method when it is clicked, pass an array of options as a parameter
$('#container').isotope({ sortBy : 'price',sortAscending : true });
//return false for the anonymous function. Not 100% sure why this is necessary but it has bitten me before
return false;
});
//removed the rest of the click methods, because it does the same thing with different params
//Here is what you are interested in understanding
//Create an empty filters object
var filters = {};
// filter buttons
//When an anchor tag with class filters is clicked, run our anonymous function
$('.filters a').click(function(){
//Create a variable that is the action anchor element
var $this = $(this);
// don't proceed if already selected by checking if a class of "selected" has already been applied to the anchor
if ( $this.hasClass('selected') ) {
return;
}
//Create an optionSet Variable, point it to the anchor's parent's class of "option-set"
var $optionSet = $this.parents('.option-set');
// change selected class
//Inside the optionSet, find elements that match the "selected" class and then remove the "selected class"
$optionSet.find('.selected').removeClass('selected');
// set this (the anchor element) class to "selected"
$this.addClass('selected');
// store filter value in object
// create a variable called 'group' that points to the optionsSet variable and grab the data-filter-group expando attribute
var group = $optionSet.attr('data-filter-group');
//Append to the filters object at the top of this section and set the data-filter-group value to the anchor tag's data-filter value
filters[ group ] = $this.attr('data-filter');
//create an isoFilters array variable
var isoFilters = [];
//Loop through each one of the items in filters (give the item an alias variable called 'prop'
for ( var prop in filters ) {
//push the prop into the isoFilters array (the opposite is pop)
isoFilters.push( filters[ prop ] )
//keep looping until there are no more items in the object
}
//create a variable called selector and turn the array into a string by joining all of the arrays together
var selector = isoFilters.join('');
//Like always, call the 'isotope' method of the 'container' element and pass our newly concatenated 'selector' string as the 'filter' option.
$container.isotope({ filter: selector });
//return false for some reason (maybe someone can expand on that)
return false;
});
});
Next is your ultimate goal which is modifying the Vitrux theme to handle intersection filters.
This gets a little tricky because
You have automatically generated tags from PHP to create the category links and the Year filter. So, there will be definitely some PHP code changes.
You must convert the jsfiddle code to handle your PHP changes
Try it using jQuery noconflict. In effect, replace any "$" with "jQuery" and see if it works.
Wordpress doesn't play well with the dollar sign.