I have a collection of dates that I will use to instanciate my jquery datepicker widget. I have used beforeShowDay method to add a highlight css class to show on what days events are. The issue I encounter is that the css class is reset when I click on a date. Am I doing something wrong ?
Thanks
$("#datepicker").datepicker({
inline: true,
showOtherMonths: true,
showButtonPanel: false,
beforeShowDay: function(date) {
var result = [true, '', null];
var matching = $.grep(events, function(event) {
return event.date.valueOf() === date.valueOf();
});
if (matching.length) {
result = [true, 'highlight', null];
}
return result;
},
onSelect: function(dateText) {
}
});
Try this way, maybe you are not returning "true".
beforeShowDay: function(dates) {
for (i = 0, vetorLen = freedays.length; i < vetorLen; i++) {
if ($.inArray(dates,freedays) != -1) {
return [true, 'css-class-to-highlight', ''];
} else {
return [false, '', ''];
}
}
return [true];
},
hope this help you.
Related
I am working on fullcalendar events and need to show a confirmation box based on some condition once on UI, when events overlap.
The action performed in this popup will decided whether to overlap an event or not.
Issue is, eventOverlap callback is called several times, even when the dragged event is hover over the other event, generating numerous calls.
Is there any way to get both static and moved event only once?
this.calenderOptions = {
contentHeight: 300,
plugins: [resourceTimelinePlugin, interaction],
editable: true,
header: {
left: "prev,next",
center: "title",
right: "prev"
},
resources: this.resources,
defaultView: "resourceTimelineDay",
timeZone: "local",
droppable: true,
eventReceive: info => {
const ref = this.dialogService.open(ConfirmationPopup, {
header: "Confirm Action",
width: "20%",
contentStyle: { "max-height": "600px", overflow: "auto" }
});
ref.onClose.subscribe(data => {
if (data === "no") {
info.event.remove();
} else if (data === "yes") {
this.row_id = info.draggedEl.getAttribute("row-id");
return true;
} else {
info.event.remove();
return false;
}
});
},
eventOverlap: (stillEvent, movingEvent) => {
// need to open a popover here only once.
console.log("eventOverlap");
return true
},
eventDrop : info => {
if(info['newResource'] && info['oldResource']){
if(info['newResource']['id'] != info['oldResource']['id']){
info.revert();
}
}
console.log("eventDrop")
},
eventClick : info => {
console.log(info);
},
drop:info => {
console.log("drop");
},
slotLabelFormat: {
hour: "2-digit",
minute: "2-digit",
meridiem: false,
hour12: false
}
};
new ThirdPartyDraggable(gridEL, {
itemSelector: ".ag-row",
eventData: eventEl => {
let rowId = eventEl.getAttribute("row-id");
let data = this.commonService.grid_service.getRowNode(rowId);
let color = null;
if(data.data["status"] == 1){
color = "#DEC181";
}
else if (data.data["status"] == 2){
color = "#A56124";
}
else {
color = "#000000";
}
return {
title: data.data["shipmentId"],
duration: data.data["est"],
color : color,
className: "test",
extendedProps: [
{
est: data.data["est"],
shipmentId: data.data["shipmentId"]
}
]
};
}
});
this.calendar = new Calendar(
this.calenderComponent.element.nativeElement,
this.calenderOptions
);
this.calendar.render();
I had a similar issue with one project at work.
In my case I have to check if an event (insert by the user with a datepicker component) overlap in the calendar or not.
So, my idea is to bypass the eventOverlap handler and use the eventDrop handler for call a function that check if the new event overlap or not others.
The function that I used in my case is the one below:
// dateStart and dateEnd have to be date strings in ISO format (ex. 1980-10-10)
function isOverlapping(dateStart, dateEnd) {
var calendarEvents = $('#calendar').fullCalendar('clientEvents');
dateStart.setHours(0, 0, 0, 0);
dateEnd.setHours(0, 0, 0, 0);
var eventStart;
var eventEnd;
for (i in calendarEvents) {
if (calendarEvents[i].id != 1 && calendarEvents[i].type == undefined) {
eventStart = new Date(calendarEvents[i].start._d);
eventStart.setHours(0, 0, 0, 0);
eventEnd = new Date(calendarEvents[i].end._d);
eventEnd.setHours(0, 0, 0, 0);
if (eventStart.getTime() < dateEnd.getTime() && eventEnd.getTime() > dateStart.getTime()) {
return true;
}
}
}
return false;
}
With the result of this function you can check if your event overlap others or not, so you can show your confirmation box only if the event overlap and only one time.
Sorry for my bad english, I hope my answer can be useful!
Context: I have a list of posts with tags, categories from wordpress api. I display these posts with Vue and using computed with a search box to filter the result based on titre, description, tags, and categories
Problem: I am trying to update a computed list when user click on a list of tag available. I add the get and set for computed data like this:
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
filterPosts: []
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
});
},
set: function (newValue) {
console.log(newValue);
this.filterPosts = Object.assign({}, newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.filterPosts = self.posts.filter(function(post){
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return tags.indexOf(tag.toLowerCase()) !== -1;
});
}
}
}); // Vue instance
The console.log always output new data based on the function I wrote on methods but Vue didn't re-render the view. I think I didn't do the right way or thought like Vue. Could you please give some insight?
Edit 1
Add full code.
I tried to add filterPosts in data but I received this error from Vue: The computed property "filterPosts" is already defined in data.
Your setter is actually not setting anything, it only logs the new value. You need to store it somewhere.
For example you can store it in the component's data:
data: {
value: 'foo',
},
computed: {
otherValue: {
get() { /*...*/ },
set(newVal) { this.value = newVal },
},
},
But this is definitely not the only possibility, if you use Vuex, the setter can dispatch an action that will then make the computed value get updated. The component will eventually catch the update and show the new value.
computed: {
value: {
get() {
return this.$store.getters.externalData;
},
set(newVal) {
return this.$store.dispatch('modifyingAction', newVal);
},
},
},
The bottomline is you have to trigger a data change in the setter, otherwise your component will not be updated nor will it trigger any rerender.
EDIT (The original answer was updated with full code):
The answer is that unless you want to manually change the list filteredPosts without altering posts, you don't need a get and set function for your computed variable. The behaviour you want can be acheived with this:
const vm = new Vue({
data() {
return {
search: '',
posts: [],
// these should probably be props, or you won't be able to edit the list easily. The result is the same anyway.
};
},
computed: {
filteredPosts() {
return this.posts.filter(function(post) {
... // do the filtering
});
},
},
template: "<ul><li v-for='post in filteredPosts'>{{ post.content }}</li></ul>",
});
This way, if you change the posts or the search variable in data, filteredPosts will get recomputed, and a re-render will be triggered.
After going around and around, I found a solution, I think it may be the right way with Vue now: Update the computed data through its dependencies properties or data.
The set method didn't work for this case so I add an activeTag in data, when I click on a tag, it will change the activeTag and notify the computed filterPost recheck and re-render. Please tell me if we have another way to update the computed data.
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
tags: [],
activeTag: ''
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
var activeTag = self.activeTag;
if (activeTag !== '') {
return tags.indexOf(activeTag.toLowerCase()) !== -1;
}else{
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
}
});
},
set: function (newValue) {
console.log(newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.activeTag = tag;
}
}
}); // Vue instance
Try something like:
data: {
myValue: 'OK'
},
computed: {
filterPosts: {
get: function () {
return this.myValue + ' is OK'
}
set: function (newValue) {
this.myValue = newValue
}
}
}
More:
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter
I have a directive which should allow me to change a specific mode, updating the scope as well.
This works fine.
The issue I'm facing now is that I need to be able to hide the element for the current mode.
I'll explain it better:
I have modes array:
[1,2,3,4,5]
and some links inside an ng-repeater:
<a href=""
ng-repeat="m in zoneModes"
ng-click="changeZoneMode({{m}})"
id="{{m}}"
class="menu-item">
<i class="icon-mode-{{m}}"></i>
</a>
what I have to do is hide an element if selectedZone is not equal to m:
ng-if="selectedMode!=m"
It seams like everything is fine doing this, but it is not.
You can clearly see what's the problem from this live example
Basically, everytime the condition is meet, it removes the element and never put it back.
I also tried with ng-show, but the result is not good either as it leaves an empty spot (you can check this other example)
Any suggestions? Thanks
EDIT
Trying to use the filter as suggested, but with no luck:
.directive('mySelector', function() {
var changeMode, linkFunction;
changeMode = function(newmode) {
var $scope;
$scope = this.$parent;
$scope.selectedMode = newmode;
$('.menu-open').attr('checked', false);
};
linkFunction = function(scope, element, attrs) {
scope.changeMode = changeMode;
scope.changeZoneMode = changeMode;
scope.zoneModes = [1, 2, 3, 4, 5];
return scope.$watch((function() {
return scope.selectedMode;
}), function(newMode) {
return scope.selectedMode = newMode;
});
};
return {
restrict: 'E',
scope: {
selectedMode: '=currentmode',
id: '#id'
},
replace: true,
templateUrl: 'templates/template.html',
link: linkFunction,
controller: 'abc'
};
}).controller('abc', ['$scope', function($scope) {
$scope.checkM = function (mode, m) {
return mode == m;
};
}])
template.html
<a href=""
ng-repeat="m in zoneModes|filter:checkM(selectedMode,m)"
ng-click="changeZoneMode({{m}})"
id="{{m}}"
class="menu-item">
<i class="icon-mode-{{m}}"></i>
</a>
Have you tried using a filter on your ng-repeat? If you use a filter your element is not lost, changing the value of m will show it again. Try something like this:
ng-repeat="m in zoneModes|filter:checkM(selectedMode,m)"
Then in your controller you need to make a filter:
$scope.checkM = function (mode, m) {
return mode == m;
};
I might not quite understand your question but is ng-if="selectedMode!='m'" giving you the result you want?
At the end , I managed to solve this using a filter, and here's the final code:
angular.module('App', ['ionic'])
.controller('ctrl', ['$scope', function($scope) {
$scope.current = {
'id': 0,
'mode': 1
}
}])
.directive('mySelector', function() {
var changeMode, linkFunction;
changeMode = function(newmode) {
var $scope;
$scope = this.$parent;
$scope.selectedMode = newmode;
$('.menu-open').attr('checked', false);
};
linkFunction = function(scope, element, attrs) {
scope.changeMode = changeMode;
scope.changeZoneMode = changeMode;
scope.zoneModes = [1, 2, 3, 4, 5];
return scope.$watch((function() {
return scope.selectedMode;
}), function(newMode) {
return scope.selectedMode = newMode;
});
};
return {
restrict: 'E',
scope: {
selectedMode: '=currentmode',
id: '#id'
},
replace: true,
templateUrl: 'templates/template.html',
link: linkFunction,
};
})
.filter('abc', function() {
return function( m, sel ) {
console.log('abc',arguments);
var filteredModes = [];
angular.forEach(m, function(mode) {
if( sel != mode ) {
filteredModes.push(mode);
}
});
return filteredModes;
}
});
and it is now working as expected.
I'm using the jQuery UI datepicker to allow the user to select a date. I need to color 7 days after the selected date.
For example, if the user has selected 1.1.2015, the days 2.1.2015 to 8.1.2015 shall be colored upon click (on the 1.1.2015). I was following this guide but I am not able to make this work.
Basically what I'm doing is creating an array of future dates (based on the calculation of milliseconds from the selected date + 86400000 milliseconds for each day), and then trying to apply css class on this array. Please advise.
EDIT:
Maybe I should have mentioned, but this picker is inline so the changes must take place instantly.
EDIT2:
Here's an example via jsfiddle.
JS:
var arrayOfFollowingWeekDates = [];
var selectedStartingDate;
//date picker configuration
$('#datepicker').datepicker({
onSelect: function(dateText, inst){
selectedStartingDate = dateText;
var selectedDateAsObject = $(this).datepicker('getDate');
arrayOfFollowingWeekDates = calcFollowingtWeekDates(selectedDateAsObject);
if(selectedDateAsObject > new Date){
console.log(selectedStartingDate);
}else{
console.log("bad date.");
}
},
inline: true,
showOtherMonths: true,
beforeShowDay: function(day, date){
var day = day.getDay();
if (day == 5 || day == 6){
return [false, ""];
} else {
return [true, ""];
}
var highlight = arrayOfFollowingWeekDates[date];
if (highlight) {
return [true, "event", highlight];
} else {
return [true, '', ''];
}
}
});
//this creates an array of the desired week, based on date objects
function calcFollowingtWeekDates(selectedDateObj){
var followingWeek = [];
var tempArrayOfNextDates = [];
var selectedDateInMilliseconds = selectedDateObj.getTime();
console.log("selected date in milliseconds: "+selectedDateInMilliseconds);
tempArrayOfNextDates[0]=selectedDateInMilliseconds;
var day;
var prevDay=selectedDateInMilliseconds;
for(var i=0;i<7;i++){
tempArrayOfNextDates[i] = 86400000 + prevDay;
day = new Date(tempArrayOfNextDates[i]);
prevDay = tempArrayOfNextDates[i];
followingWeek[i]=day;
console.log("next day is : "+ day);
}
return followingWeek;
}
CSS:
.event a {
background-color: #42B373 !important;
background-image :none !important;
color: #ffffff !important;
}
Here is a working fiddle: http://jsfiddle.net/97L93a3h/2/
var selectedDay = new Date().getTime();
$('.datepicker').datepicker({
onSelect: function (date) {
selectedDay = $(this).datepicker('getDate').getTime();
},
beforeShowDay: function (date) {
var d = date.getTime();
if (d > selectedDay && d < selectedDay + 1 + 86400000 * 7) {
return [true, 'ui-state-highlight', ''];
} else {
return [true, ''];
}
}
});
Merge this method into your existing script.
I want to remove a class on an element from within a $watch. The class would be removed after a $timeout of a 10 seconds.
Code looks something like this:
Controller:
$scope.$watch('lastPrice', function(newVal, oldVal){
if ( newVal > oldVal ) {
$scope.lastTick = 'up';
} else if ( newVal < oldVal ) {
$scope.lastTick = 'down';
} else {
$scope.lastTick = 'none';
}
$scope.last = newVal;
$timeout(function(){
//remove $scope.lastTick class name from element here
}, 10000)
});
View:
<span class="last" ng-class="lastTick">{{lastPrice}}</span>
I would avoid using the delete operator here since it would mean that the lastTick could be accidentally inherited from the parent scope. Instead, I would recommend:
$timeout(function () { $scope.lastTick = undefined; }, 10000);
Demo: http://jsfiddle.net/9ebjt/1/
Try delete operator:
$timeout(function(){
delete $scope.lastTick;
}, 10000);
JSFiddle: http://jsfiddle.net/cherniv/9ebjt/