I have made a CustomEditorTemplate for a Scheduler. And there is one thing that does not work as expected.
For the RecurrenceEditorFor, it has an End On, where you can select a date. But for our RecurrenceEditorFor, it has automatically set the Start to the current date, and not the selected date of the scheduler.
Our RecurrenceEditorFor looks like:
<div data-container-for="recurrenceRule" class="k-edit-field">
#(Html.Kendo().RecurrenceEditorFor(model => model.RecurrenceRule).Messages(m => SchedulerHelper.MessageLocaliztion(this, m))
.HtmlAttributes(new { data_bind = "value:recurrenceRule" })
)
</div>
I could, of cause set the Start to a specific date,
<div data-container-for="recurrenceRule" class="k-edit-field">
#(Html.Kendo().RecurrenceEditorFor(model => model.RecurrenceRule).Messages(m => SchedulerHelper.MessageLocaliztion(this, m))
.HtmlAttributes(new { data_bind = "value:recurrenceRule" })
.Start(new DateTime(2014,8,4))
)
</div>
, but this would not work, if I select a date before this one.
EVEN if I select a date after this current date, it will give me this date. So it is not because this current date is a minimum, but it must be a static date, set somewhere, or somehow.
I have also tried to set the text in the input field with some JavaScript, but this will not make it possible to select a date before the current date. And this will also mess up the RecurrenceRule.
I can see it works as standard, but not for us, after we have added the CustomEditorTemplate. Do we miss something, or made something wrong ?
I found a work around for this:
I added a click method on the Recurrencerule div:
$("div[name='RecurrenceRule']").on("click", function () {
window.changerecurrenceStart();
});
and the methochangerecurrenceStart :
function changerecurrenceStart()
{
var date = $("#startDate").data("kendoDatePicker")._value;
$("#RecurrenceRule").data("kendoRecurrenceEditor").options.start = date;
$("#RecurrenceRule").data("kendoRecurrenceEditor").options.value = date;
}
This worked well for me.
Related
I'm having a little issue with FullCalendar v5, I configured it with the dayGridMonth View and I would like to listen when the user changes the current month ...
For example, if he is seeing February and click on next he'll see march, so I was expected for a handler like onChange or onMonthChange but I didn't find anything in the documentation to do something like this ...
I figured how to get around the problem by making my own prev / next buttons and triggering my custom handler on the click ... But I would like to know if there is a vanilla way to do it?
Thanks for your answers.
As mentionned by #Devsi Odedra, the answer was datesSet
The doc : https://fullcalendar.io/docs/datesSet
This is my actual code if it can help someone :
new Calendar(document.getElementById("calendar"), {
plugins: [ dayGridPlugin, interactionPlugin ],
datesSet: event => {
// As the calendar starts from prev month and end in next month I take the day between the range
var midDate = new Date((event.start.getTime() + event.end.getTime()) / 2).getMonth()
var month = `0${ midDate.getMonth() + 1 }`.splice(0, -1)
doSomethingOnThisMonth(month, midDate.getFullYear())
}
});
function doSomethingOnThisMonth(month, year) {
fetch(`myApi.com/something?month=${ month }&year=${ year }`)
.then((result) => {
// Do something
})
}
I have implemented the datepicker and timepicker with add,delete buttons in each row. When I click on add, will add new row and delete will delete row.
I have the code link https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
But how to handle the state for multiple datepicker and timepicker,
When change the date, it doesnot reflect the change in field.
https://codesandbox.io/s/zen-water-tfyoz?fontsize=14&hidenavigation=1&theme=dark
So the problem is in your renderRowData function:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state.pickdate}
/>
)
For value u use this.state.pickdate, but when value changes you set it with:
handleDatePicker = (value, name, field, row) => {
this.props.handleInputChange(value, field, row);
console.log("data", value, "for", name);
this.setState({ [name]: value });
};
wich means that your state is now:
{
["pickdate_" + rowId]: value // where row is selected row
}
you need to change your datepicker to access value like this:
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId === "pickdate" ? (
<DatePicker
locale="en-GB"
className="datepicker"
name={"pickdate_" + rowId}
onChange={e =>
this.handleDatePicker(
e,
"pickdate_" + rowId,
column.dataFieldId,
row
)
}
value={this.state["pickdate_" + rowId] || this.defaultPickDate} // this will take new selected value or default if there is none
/>
)
Working example: https://codesandbox.io/s/funny-fog-1v9o3
The reason that the date field's changes aren't reflected in the UI is that you've implemented it as a controlled component (you are setting the value of each DatePicker based on the corresponding value in the state and updating the state when you change the value, which effectively synchronizes the component with the state, more or less), whereas the TimePickers do change in the UI when a new time is chosen because they are implemented as uncontrolled components. Controlled components are often the best method, but your date component isn't updating on change because there are problems in your handleDatePicker function, as pointed out by Kaca992's answer.
If you don't know much about controlled vs uncontrolled components, see here.
As for how to handle the state for multiple DatePickers and TimePickers, personally I'd recommend that you store them as an array of rows in the state. For example, this would be a default state:
this.state = {
rows: [
{
date: new Date(),
start: moment(),
end: moment(),
}
]
}
Each row element in the array would correspond to a row in the table, and you can just use array.map to render each row as a component that contains the DatePicker, TimePickers, and buttons, and then just send the array index along with the new value to your onChange functions so that the correct row's changes can be reflected in the new state.
This would require a bit of a re-write of your DynamicDateTimePicker class, but the logic would be much simpler and more readable than how you currently have it structured.
I have created a custom calendar view for one of my projects using angular. I tried to use a full calendar library but it doesn't give many customization options. This is my custom view in stackblitz. I want to render events. but I don't understand how to do that.
my expectation is this
it's only a idea. You has events like
events:any[]=[{
startDate:'2020-01-06',
endDate:'2020-01-08',
event:'Hello word'
}]
When you render the calendar add "data attributes" and put a template variable reference to allow us to get it using ViewChildren -get the "row" because will be important when a event call in two differents weeks. It's interesting you give as data-day attribute the day you show. For this create a function like
getDate(day:any,row:number)
{
if (day.isSameMonth)
return this.currentMonth.getFullYear()+'-'+('0'+(this.currentMonth.getMonth()+1)).slice(-2)
+'-'+('0'+day.day).slice(-2)
if (row==0)
{
return this.currentMonth.getMonth()?
this.currentMonth.getFullYear()+'-'+
('0'+this.currentMonth.getMonth()).slice(-2)+'-'+
('0'+day.day).slice(-2):
(this.currentMonth.getFullYear()-1)+'-12-'+('0'+day.day).slice(-2)
}
return this.currentMonth.getMonth()!=11?
this.currentMonth.getFullYear()+'-'+
('0'+this.currentMonth.getMonth()+2).slice(-2)+'-'+
('0'+day.day).slice(-2):
(this.currentMonth.getFullYear()+1)+'-01-'+('0'+day.day).slice(-2)
}
<div #daysCell
[attr.data-day]="getDate(days,i)"
[attr.data-row]="i"
[ngClass] = "{'today-cell':days.isToday === true}">
{{days.day}}
</div>
Render the events in a div as
<div *ngFor="let evento of events;let i=index">
<div #event [attr.data-event]="i" class="event">{{evento.event}}</div>
</div>
Well, the funny part. We get the "cells" and the "events" as
#ViewChildren('daysCell') cells:QueryList<ElementRef>
#ViewChildren('event') eventsCells:QueryList<ElementRef>
And in a function renderEvent you use Renderer2 to give position left top-rigth to your elements
renderEvents(){
const cells=this.cells.map(x=>{
return {
pos:x.nativeElement.getBoundingClientRect(),
day:x.nativeElement.getAttribute('data-day'),
row:x.nativeElement.getAttribute('data-row'),
}
})
this.eventsCells.forEach((x:any,index:number)=>{
const cellStart=cells.find(c=>c.day==this.events[index].startDate)
const cellEnd=cells.find(c=>c.day==this.events[index].endDate)
this.render.setStyle(x.nativeElement,'top',(cellStart.pos.top+1)+'px')
this.render.setStyle(x.nativeElement,'left',(cellStart.pos.left+1)+'px')
this.render.setStyle(x.nativeElement,'width',
(cellEnd.pos.left-cellStart.pos.left+cellEnd.pos.width)+'px')
})
}
Well, it's only an aprox. You need check if the event are in two weeks, if a event it's showed or not... and another ajusts (I hope minnor adjusts)
You can see in stackblitz
Update if we want to place more than one event in a date, we need add a propertie to the "events", e.g. we can called "row", so, the "top" becomes like
this.render.setStyle(
x.nativeElement,
"top",
(this.events[index].row
? this.events[index].row *
(2 + x.nativeElement.getBoundingClientRect().height) +
cellStart.pos.top
: cellStart.pos.top + 1) + "px"
);
Now, the dificult is know how give the value to "row". For this, we need loop over the events, store in an array the days that are ocupped and, if one is ocuped increment the row. puff. make a function
getEventsRows() {
const datesOcupped: any[] = [];
this.events.forEach(x => {
x.row = 0;
const startDate = new Date(x.startDate).getTime();
const endDate = new Date(x.endDate).getTime();
for (let date = startDate;date <= endDate;date = date + 24 * 60 * 60 * 1000) {
const dateOcupped = datesOcupped.length?
datesOcupped.find(x => x.time == date): null;
if (!dateOcupped) {
datesOcupped.push({
time: date,
row: 0
});
} else dateOcupped.row++;
x.row = dateOcupped? dateOcupped.row > x.row?
dateOcupped.row: x.row: 0;
}
});
}
I have a range date picker at the moment.
<input id="rangeDatepicker2" class="g-font-size-12 g-font-size-default--md" type="text" data-rp-wrapper="#rangePickerWrapper2" data-rp-type="range" data-rp-date-format="d M Y" data-rp-default-date='["01 Jan 2016", "31 Dec 2017"]'>
I would like to reuse the dates but I am unable to select them for all scenarios. I tried the code below which works great if the date selected are in the same month. But my issue is this bit of code doesn't work if the dates span multiple months.
$("#rangeDatepicker2").change(function () {
var dates = $('.selected');
if (dates.length == 2) {
var start = dates[0].dateObj;
var end = dates[1].dateObj;
//interact with selected dates here
}
});
May I ask how do I properly grab selected date range of a flatpickr.
Many input plugins actually pass the values in a onChange handler. The values are available there.
Solution
Pass down an onChange handler when initializing flatpickr:
$("#rangeDatepicker2").flatpickr({
mode: 'range',
onChange: function(dates) {
if (dates.length == 2) {
var start = dates[0];
var end = dates[1];
// interact with selected dates here
}
}
})
Demo
onChange documentation:
https://flatpickr.js.org/events/#hooks
Any who had worked with the ElementUI library for Vuejs.
Im using the el-time-picker component, right now when i focus it auto set the input with the current hour, but i want it to keep it blank, i had tried seting it default value to null but it doesn't work.
Also even thought i have this:
:picker-options="{
format: 'HH:mm',
}"
The picker dropdown just show up hour and minutes but once selected, the input display hour:minutes:seconds, this also produce that if i type the hour, i gotta type it with seconds for get it to work, i don't want this behavior, just to display hh:mm and be able to type just it.
Any suggestions:? I'm trying to understand the code as right to try to overwrite the component.
Edit 1
Time picker code is simple, i just has the component with a few options:
<el-time-picker
v-model="row.from"
value-format="HH:mm"
:picker-options="{
format: 'HH:mm',
selectableRange: '00:00:00 - 23:59:00',
}"
placeholder="Desde">
</el-time-picker>
The initial (default) value for row.from must be an empty String, not null. The format option works only for the popup selector, not for the editbox. To achieve what you want you have to specify the step option like in this JSfiddle
UPDATE
The JSfiddle was updated.
Use the #focus event to change the default value:
https://jsfiddle.net/2q8ovLah/3/
<el-time-picker placeholder = "HH:mm:ss"
value-format = "HH:mm:ss"
v-model = "time"
:editable = "false"
:picker-options = "{selectableRange: '00:00:01 - 23:59:59'}"
clearable
#focus = "focused">
</el-time-picker>
data() {
return {
time: ''
};
},
methods: {
focused(){
if(this.time === '') {
this.time = '00:01:00'
}
}
}