search envelope-o feed check
Home Unanswered Active Tags New Question
user comment-o

New event renders twice

Asked by Martin Säfsten
7 years ago.

So I am working with the new Angular 2 daypilot in Typescript, and I have a bug that seems to come and go as it pleases. I have checked and rechecked my code, and i feel more and more that it's either a bug in Daypilot, or there is something small that I am doing wrong.

When I add a new event to a resource row, it renders the new event twice. They are identical in every way that I can see, even the id (which is strange, since duplicate id is not allowed)

The Daypilot scheduler is in its own Angular component, and I am using a subscription on a Observable object to update the event array.

Here is the html for the scheduler:

" <daypilot-scheduler #adminscheduler [config]="config" [events]="events"></daypilot-scheduler> "

this is the code in the schedulers components ngOnInit() function:

this.adminScheduleService.getEvents().subscribe((events) => {
this.events = events;
//console.log("NrOfActions: " + this.events.length);
});

So I have a service AdminScheduleService, that has the function getEvents(), which returns a Observable<ExtendedDaypilotEvent[]>. I subscribe to that, which sends a new array of events (ExtendedDaypilotEvents has some extra info that we are using along with the mandatory properties), that is inserted into this.events, which is bound to the daypilot-scheduler tag.

That console.log shows the correct number of events (ie the new event is not counted twice)

What is extra strange here, is that if I add another event in the same manner, the double dissapears from the first event, and the new event is rendered twice.

Also, we have a zoom-slider, which changes the cell duration. If you change the zoom (which I assume triggers a full rerender), the extra rendered event dissapears.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

Martin,

How do you add the new event?

Comment posted by Martin Säfsten
7 years ago.

The full chain of event is as follows:

1. Fetch the new event from the server
2. Gets the full list of events from a cache service
3. Adds the new event to that list
4. Sends the full list through the Observable I mentioned (It is a ReplaySubject as a Observable, so I do subject.next(newEvents))
5. The schedulers component gets the new full list of events in the subscribe mentioned above
6. Inserts the new list into this.events, which is bound to the [event] binding

Comment posted by Dan Letecky [DayPilot]
7 years ago.

OK, thanks.

In this case, the suspect is the optimization that takes place when updating events. The new event set is compared to the existing events and if there are less than 10 changed events they are updated one by one. Otherwise all events are deleted and re-rendered.

So:
1. If you are using an older DayPilot version try upgrading to the latest version.
2. The diffs are calculated using an event ID which is converted to a string. There might be some quirks involved. How do your IDs look like?
3. You can try direct event update (see also Performance section at https://doc.daypilot.org/scheduler/angular-2/). If this works it would point to the optimization described above.

import {Component, ViewChild} from '@angular/core';
import {DayPilotSchedulerComponent} from "daypilot-pro-angular";

@Component({
  selector: 'angular2-scheduler-example',
  template: `<daypilot-scheduler #scheduler1></daypilot-scheduler>`
})
export class AppComponent {

  @ViewChild("scheduler1")
  scheduler: DayPilotSchedulerComponent;

  loadEvents(events) {
    var dp = this.scheduler.control;
    dp.events.list = events;
    dp.update();
  }  

}
Comment posted by Martin Säfsten
7 years ago.

Im using version 8.4.2965 at the moment.
The ids on the events are type number.

I actually just found another clue after trying some stuff out. Nothing like writing down a problem to see new potential things that can go wrong hehe.

So the code that triggers the Observable from getEvents() also triggers another Observable:

this.adminScheduleService.getSchedules().subscribe((newSchedules) => {
            this.schedules = newSchedules;
            console.log("NrOfSchedules: " + this.schedules.length);
            if (this.scheduler.control != null) {
                this.scheduler.control.resources = newSchedules;
                this.scheduler.control.update();
            }
        });

So I have a update to the event list, and at the same time a control.update().

I tried just commenting out the code that triggers this update, and there was no double rendering anymore.

So it feels like some wires gets crossed when you do these two things at the same time. Right?

Comment posted by Dan Letecky [DayPilot]
7 years ago.

Yes, that's most likely the problem. The update() method is partially asynchronous - it renders events in batches.

You could optimize the rendering by waiting for results from both getSchedules() and getEvents() and firing just one .update() which would update everything using one redraw. You just need to assign events directly to control.events.list.

Answer posted by Martin Säfsten
7 years ago.

Right,

Thanks for the help!

Comment posted by Martin Säfsten
7 years ago.

So a couple of hours of coding, and its almost working, but something is still wierd.

this is my code now. I have a boolean, updateControl, that ensures only one call to dp.update() is made. I also changed to that events are using "Direct Event Update" as you recommended.

This code is beeing executed when navigating to the route with the daypilot component in it. It is also executed when changes are made to the daypilot.

    private updateControl: boolean = false;
    private tempEventContainer: models.ExtendedDaypilotEvent[];
    private tempScheduleContainer: models.ScheduleDTO[];
    public ngAfterViewInit(): void {
        this.adminScheduleService.getSchedules().subscribe((newSchedules) => {
            if (this.updateControl) {
                this.updateDaypilot(this.tempEventContainer, newSchedules);
                this.updateControl = false;
            } else {
                this.updateControl = true;
                this.tempScheduleContainer = newSchedules;
            }
        });
        this.adminScheduleService.getEvents().subscribe((events) => {
            if (this.updateControl) {
                this.updateDaypilot(events, this.tempScheduleContainer);
                this.updateControl = false;
            } else {
                this.updateControl = true;
                this.tempEventContainer = events;
            }
        });
    }
    private updateDaypilot(events: models.ExtendedDaypilotEvent[], schedules: models.ScheduleDTO[]) {
        this.schedules = schedules;
        this.events = events;
        const dp = this.scheduler.control;
        if (dp != null) {
            dp.resources = schedules;
            dp.events.list = events;
            dp.update();
        }

    }

As I mentioned above, this is a Angular Component called AdminScheduleComponent, and it is loaded on a route as the single component.

this is the code in my route table:
{ path: 'app/adminschedule', component: AdminScheduleComponent, canActivate: [guards.AuthenticatedGuard] }

On the first trip to this route "app/adminschedule", this code is beeing executed, and everything works great. But if I navigate away, and then back to it, the resources are loaded, but the events are not.

If I debug my typescript, and set a breakpoint at the dp.update() call, and "step over" it, I can see the events. But after my code is executed, it goes into zone.js (Some angular plugin) and executes some kind of loop or queue, and the events dissapear. As you wrote above, events are loaded asynchronously, so Im guessing that this is what you were talking about.

I am developing in Chrome by the way, if that is relevant.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

I think Observable.forkJoin() is what you are looking for:

https://stackoverflow.com/questions/35566931/angular2-observable-how-to-wait-for-all-function-calls-in-a-loop-to-end-before

Untested, just copied and adjusted:

Observable.forkJoin(
  this.adminScheduleService.getSchedules(),
  this.adminScheduleService.getEvents()
)
.subscribe(received => {
  let dp = this.scheduler.control;
  dp.resources = received[0];
  dp.events.list = received[1];
  dp.update();
});
Comment posted by Martin Säfsten
7 years ago.

Thanks for that tip. I could not use forkJoin because the Observables are based on ReplaySubjects. But zip does the same thing.

I get the same problem with this solution. If I do a full F5 refresh on the route "app/adminschedule", it works great. But if I navigate away, and go back to that route, or I do a full refresh on another route, and navigate to it, I see the resources, but not the events. And I can see that it adds the events at the dp.update(), but something after it clears them again.

   public ngAfterViewInit(): void {
        Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            this.schedules = received[1] as models.ScheduleDTO[];
            this.events = received[0] as models.ExtendedDaypilotEvent[];
            let dp = this.scheduler.control;
            if (dp != null) {
                dp.resources = this.schedules;
                dp.events.list = this.events;
                dp.update();
            }
        });
    }
Comment posted by Dan Letecky [DayPilot]
7 years ago.

Does it clear the event data as well (dp.events.list)?

There are only three ways to update the event data:

1. directly using event.list
2. using [events] attribute of <daypilot-scheduler>
3. using onScroll event if dynamic loading is enabled

You can also use .events.add/update/remove but that wouldn't clear all events.

Comment posted by Martin Säfsten
7 years ago.

I added a console.log to output "control.events.list.length", but list seems to be undefined. It is undefined even if I can see the events or not.

Any other way of checking if daypilot actually has any event data?

Comment posted by Martin Säfsten
7 years ago.

So I have boiled down the two scenarios to this:

There is a service that gets the resources and events from the server, and calls resourceReplaySubject.next(newschedules), and eventReplaySubject.next(newevents). Those replaysubjects have a observable each, which is created by doing "resourceReplaySubject.asObservable()". Those two observables are the getSchedules() and getEvents() above.

ReplaySubject are like observables, but the underlying data is like a queue. When you do subject.next(object), anyone currently listening to the observable gets the new data. But if you subscribe to it AFTER subject.next has been called, you get the latest data inserted into the subject right away.

So we use .subscribe to both initialize the data, get changes to the data, and getting the current data when navigating to the component with the daypilot.

1. The scenario that seems to work fine, is when data is fetched from the server, and a subject.next(data) is called while the component is subscribed.
2. The scenario that does not work is when the subject already has data in its queue, and the subscription is created after.

The more I think about it, it feels like there is something iffy with the combination of daypilot and replaysubjects, with the underlying lifecycle of angular.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

How does your ReplaySubject implementation look like? How many items does it hold? It looks like it replays too much if you subscribe late (it sends the good result plus some invalid value/s).

I've been using something like this to implement caching and it seems to work fine. Note that the queue has just one item.

import {Injectable} from "@angular/core";
import {CalendarData, DataService} from "../backend/data.service";
import {Observable, ReplaySubject} from "rxjs";

@Injectable()
export class SchedulerHolderService {

  constructor(private ds: DataService) {
  }

  private subject = new ReplaySubject(1);

  getScheduler(forceRefresh?: boolean): Observable<CalendarData> {
    if (!this.subject.observers.length || forceRefresh) {
      this.ds.getScheduler().subscribe(
        data => this.subject.next(data.item),
        error => {
          this.subject.error(error);
          this.subject = new ReplaySubject(1);
        }
      );
    }

    return this.subject;
  }
}
Comment posted by Martin Säfsten
7 years ago.

We have a similar system with a cache, that stores data per date. The subjects responsibility is basically to have the data for one date, and if that date changes, a service gets the data from the new date, and sends it out via the replaysubject.

I am doing very small scale now during developement, so Im working with around 2 resources, and 5 events. So each subject has very little data.

We have a SharedDataService, which among other things, holds which date is currently selected. That date controls which date that the daypilot component should show. We use the Daypilot Scheduler, and the scope is one date. SharedDataService has a ReplaySubject which holds the date.

//SharedDataService
public changeDate(newDate: moment.Moment) {
        this.selection.date = newDate;
        this.selectedSelectionSubject.next(this.selection);
    }
Then we have a ScheduleService, which subscribes to the ReplaySubject from SharedDataService, and gets the currently selected date. This service checks the cache for data on the date.If it has data in the cache, it sends it in the replaysubject. If it does not have it, it gets data from the server, inserts it into the cache, then sends it down the subject. I have compressed the code under to show only relevant things.
//ScheduleService
    public adminEvents$: Observable<serverModels.ExtendedDaypilotEvent[]>;
    public adminSchedules$: Observable<serverModels.ScheduleDTO[]>;
    private adminEventsSubject = new ReplaySubject<serverModels.ExtendedDaypilotEvent[]>(1);
    private adminSchedulesSubject = new ReplaySubject<serverModels.ScheduleViewModel[]>(1);
constructor(
        private cacheService: DayPilotCacheService,
        private sharedDataService: SharedDataService,
        private adminBackendService: AdminScheduleBackendService) {

        this.adminEvents$ = this.adminEventsSubject.asObservable();
        this.adminSchedules$ = this.adminSchedulesSubject.asObservable();

        sharedDataService.selectedSelection$.subscribe((newSelection) => {
            this.updateSchedules(newSelection.areaId, newSelection.date);
            this.updateEvents(newSelection.areaId, newSelection.date);
            this.currentSelection = newSelection;
        });
    }

public updateSchedules(areaId: number, selectedDate: moment.Moment) {
        this.updateAdminSchedules(areaId, selectedDate);
    }
public updateAdminSchedules(areaId: number, selectedDate: moment.Moment) {
        const schedules = this.cacheService.getSchedules(areaId, selectedDate, ScheduleMode.AdminSchedule);
        if (schedules !== null) {
            this.adminSchedulesSubject.next(schedules);
            return;
        }

        const requestData: serverModels.GetBaseSchedulesForDateRequest = {
            areaId,
            date: selectedDate.toDate(),
        };
        this.adminBackendService.getBaseSchedulesForArea(requestData)
            .subscribe((schedules) => {
                this.adminSchedulesSubject.next(schedules);
                this.cacheService.setSchedules(areaId, selectedDate, schedules, ScheduleMode.AdminSchedule);
            });
    }

Then there is AdminScheduleService, which does not have any responsibility of getting the data for the daypilot. We have two daypilot schedulers on our site, and they have a service each. AdminScheduleService, and DayScheduleService. These does not matter in this case.

    public getEvents(): Observable<serverModels.ExtendedDaypilotEvent[]> {
        return this.scheduleService.adminEvents$;
    }
    public getSchedules(): Observable<serverModels.ScheduleDTO[]> {
        return this.scheduleService.adminSchedules$;
    }

Last step is AdminScheduleComponent, which is where the scheduler is contained. it listens to the observables from AdminScheduleService.

    public ngAfterViewInit(): void {
        Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            let newSchedules = received[1] as models.ScheduleDTO[];
            let newEvents = received[0] as models.ExtendedDaypilotEvent[];
            let dp = this.scheduler.control;
            if (dp != null) {
                dp.resources = newSchedules;
                dp.events.list = newEvents;
                dp.update();
            }
            this.schedules = newSchedules;
            this.events = newEvents;
        });
    }

Comment posted by Dan Letecky [DayPilot]
7 years ago.

That seems to be fine.

However, it looks like Observable.zip() emits multiple times and the last time received[1] is fine but received[0] is empty/null/undefined.

Can you try to print the values to console before calling update()?

            if (dp != null) {
                dp.resources = newSchedules;
                dp.events.list = newEvents;
                console.log("Updating", newSchedules, newEvents);
                dp.update();
            }
Comment posted by Martin Säfsten
7 years ago.

I did JSON.stringify so I could paste it in here. First box is newSchedules, second is newEvents

Updating
[
  {
    "id": 478380,
    "name": "test",
    "areaId": 261
  },
  {
    "id": 478383,
    "name": "testtests3a",
    "areaId": 261
  }]
[
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 07:40:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146032,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Bengtsson, Sif Linnea",
    "resource": 478380,
    "start": "2017-09-02 07:00:00",
    "searchText": "Sif Linnea Bengtsson 192705134845",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 05:10:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146034,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478380,
    "start": "2017-09-02 05:00:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 06:10:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146035,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Dahlbom, Billy",
    "resource": 478380,
    "start": "2017-09-02 06:00:00",
    "searchText": "Billy Dahlbom 193311305415",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 06:50:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146044,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Bengtsson, Sif Linnea",
    "resource": 478380,
    "start": "2017-09-02 06:20:00",
    "searchText": "Sif Linnea Bengtsson 192705134845",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 08:20:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146045,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478380,
    "start": "2017-09-02 08:00:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-02 03:10:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146046,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Andersson, Ulla",
    "resource": 478380,
    "start": "2017-09-02 03:00:00",
    "searchText": "Ulla Andersson 192807025402",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 2,
    "areaId": 261,
    "end": "2017-09-02 06:30:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146047,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "&nbsp;",
    "resource": 478383,
    "start": "2017-09-02 06:00:00",
    "searchText": null,
    "cssClass": "event_misctime",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  }
]
Comment posted by Dan Letecky [DayPilot]
7 years ago.

OK, so that part seems to be fine. It emits just once and the data is valid.

I'd recommend saving this.scheduler.control to the global scope temporarily (e.g. in ngAfterViewInit) so you can access it in the JS console:

window["dp"] = this.scheduler.control;

Then you can check the control state when it's invalid (missing events). Especially:

dp.events.list //  should print the array as above
dp.startDate // should make "2017-09-02" visible

I also assume that you have cleared the [events] attribute in <daypilot-scheduler> because it was bound to this.events in your first listing and you save newEvents there after updating the control manually.

Comment posted by Martin Säfsten
7 years ago.
Yeah, I cleared the events attribute. I did try at one point to have both that binding, and updating the eventlist directly, and every event rendered double. Easy to spot that error.
<daypilot-scheduler #adminscheduler [config]="config"></daypilot-scheduler>

window["dp"].events.list is undefined
window["dp"].startDate is DayPilot.Date with value 2017-09-02T00:00:00

So the event list is undefined. But it is undefined in both of the scenarios I mentioned above. IE its undefined, even if I see the events. Thats pretty wierd...

Comment posted by Dan Letecky [DayPilot]
7 years ago.

OK, we are getting closer.

I've created a testing project that uses direct event loading instead of the [events] attribute. It worked fine and I didn't inspect it further.

It turned out that the events attribute value is used even if the attribute is not specified (an obvious bug). The value is of the target is "undefined". Now when you inspected dp.events.list it also turns out that the testing project also doesn't store any events in dp.events.list (it's undefined as well). It shows the events correctly though which masks the problem.

In your case, if event loading takes a while the undefined value is applied first and gets overwritten by the real data. However, if the data is already available it gets overwritten by undefined from the missing attribute.

Anyway, the [events] attribute behavior should be fixed now in the latest build (8.4.2968). The problem can't be reproduced in the testing project anymore. Could you please give it a try?

Comment posted by Martin Säfsten
7 years ago.

initial tests looks good.

this.events is bound through the [events] attribute.

At the start of this adventure, you said that updating the attribute bound event list and doing a console.update() at the same time was a bad idea. Is this still the case here in my code?

Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            let newSchedules = received[1] as models.ScheduleDTO[];
            let newEvents = received[0] as models.ExtendedDaypilotEvent[];
            this.schedules = newSchedules;
            this.events = newEvents;

            let dp = this.scheduler.control;
            if (dp != null) {
                dp.resources = newSchedules;
                console.log("Updating", JSON.stringify(newSchedules), JSON.stringify(newEvents));
                dp.update();
                window["dp"] = dp;
            }

        });
Comment posted by Martin Säfsten
7 years ago.

After further testing, this is not working quite right.

If I add a new event, it gets inserted ok. But If I move that event, it creates a copy of that event and moves one of them to the new time. So you get the same event at both the new time, and at the old time.

Same symptoms if I move a event, then move it again. First move is good, second move creates the copy.

If I move a event, then move a second event, a copy is created of the first event at the same time.

The copies does not come from the data. There is not double events in the array I put in. If there was, it would probably throw exceptions of duplicate Ids, which it does not do.

Also, in one of our two daypilots, If I move/change/add a visit, the class that I specified in the cssClass property is not set to the event. I am not sure why it only happens on one of them, but I will investigate that further myself, to see if there is any differences. There is no differences in the new event data. cssClass property is correct on both of them.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

It looks like if the rendered event is not in sync with the underlying data. I've never seen that but it looks like the source of the problem is that events are updated using the attribute and the resources are updated directly.

At this moment, I'd like to see if this is really the cause. Can you please try the following options:

1. Update both events and resources using attributes:

Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            let newSchedules = received[1] as models.ScheduleDTO[];
            let newEvents = received[0] as models.ExtendedDaypilotEvent[];

            this.events = newEvents;
            this.config.resources = newSchedules;
        });

This will let Angular do both changes during one ngDoCheck cycle. Just one update() will be performed internally.

2. Update both events and resources directly:

Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            let newSchedules = received[1] as models.ScheduleDTO[];
            let newEvents = received[0] as models.ExtendedDaypilotEvent[];

            let dp = this.scheduler.control;
            if (dp != null) {
                dp.resources = newSchedules;
                dp.events.list = newEvents;
                console.log("Updating", JSON.stringify(newSchedules), JSON.stringify(newEvents));
                dp.update();
                window["dp"] = dp;
            }

        });

This is what you already tried but now it will be affected by the fix included in 2968.

And a final question:

  • You are talking about two instances of the Scheduler. Are they both displayed at the same time, sharing the data source?
Comment posted by Dan Letecky [DayPilot]
7 years ago.

Just to make sure:

For #1 above the <daypilot-scheduler> tag should look like this:

<daypilot-scheduler #adminscheduler [config]="config" [events]="events"></daypilot-scheduler>

For #2 it should look like this:

<daypilot-scheduler #adminscheduler [config]="config"></daypilot-scheduler>
Comment posted by Martin Säfsten
7 years ago.

Hey,

The first solution did not work. It never rendered any resources or events after a F5 (When subscription was on during subject.next. It did work when navigating to it.

The second solution seems to do the trick, after initial testing. No wrongly rendered events, and it renderes the events on both direct subject.next call, and late subscription.

I'll continue testing and notify you if the problem reappears.

As for the two schedulers, they are in separate components, on separate routes, and separate observers, and separate cache.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

Great, thanks for the update. Meanwhile, I have found a flaw in the event update logic that reads the [events] attribute value and applies the optimized event update. That would fully explain the duplicate event from your initial post. A fix is on the way.

However, that doesn't explain why the resources wouldn't get updated when using scenario #1. I've tried many different setups with a ReplaySubject but I wasn't able to reproduce the problem.

Could you please try to print the received values in scenario #1?

Observable.zip(
            this.adminScheduleService.getEvents(),
            this.adminScheduleService.getSchedules()
        ).subscribe(received => {
            let newSchedules = received[1] as models.ScheduleDTO[];
            let newEvents = received[0] as models.ExtendedDaypilotEvent[];

            console.log("Updating", JSON.stringify(newSchedules), JSON.stringify(newEvents));
                
            this.events = newEvents;
            this.config.resources = newSchedules;
        });

I'd like to check whether the zip() method emits the values correctly.

Comment posted by Martin Säfsten
7 years ago.

I dont see anything difference in the data. First print is after a F5 refresh. Second is after a navigation to the route with the component. Feels more like a timing or initialization thing than a data problem.

zip that does not render any resources or event:

Resources
[
  {
    "id": 478393,
    "name": "test",
    "areaId": 261
  },
  {
    "id": 478396,
    "name": "gjj",
    "areaId": 261
  }
]

Events
[
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 02:00:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146078,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cissig, Airi",
    "resource": 478396,
    "start": "2017-09-03 01:30:00",
    "searchText": "Airi Cissig 193304149408",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 03:10:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146080,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Andersson, Ulla",
    "resource": 478393,
    "start": "2017-09-03 02:50:00",
    "searchText": "Ulla Andersson 192807025402",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 05:40:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146083,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478393,
    "start": "2017-09-03 04:20:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 04:50:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146086,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478396,
    "start": "2017-09-03 04:10:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 07:50:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146088,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Alfredsson, Margareta",
    "resource": 478393,
    "start": "2017-09-03 07:20:00",
    "searchText": "Margareta Alfredsson 193005252428",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 07:40:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146090,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Bengtsson, Sif Linnea",
    "resource": 478396,
    "start": "2017-09-03 06:40:00",
    "searchText": "Sif Linnea Bengtsson 192705134845",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  }
]

zip that does render resources and events:

Resources
[
  {
    "id": 478393,
    "name": "test",
    "areaId": 261
  },
  {
    "id": 478396,
    "name": "gjj",
    "areaId": 261
  }
]

Events
[
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 02:00:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146078,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cissig, Airi",
    "resource": 478396,
    "start": "2017-09-03 01:30:00",
    "searchText": "Airi Cissig 193304149408",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 03:10:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146080,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Andersson, Ulla",
    "resource": 478393,
    "start": "2017-09-03 02:50:00",
    "searchText": "Ulla Andersson 192807025402",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 05:40:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146083,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478393,
    "start": "2017-09-03 04:20:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 04:50:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146086,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Cahlås , Karin",
    "resource": 478396,
    "start": "2017-09-03 04:10:00",
    "searchText": "Karin Cahlås  192508144801",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 07:50:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146088,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Alfredsson, Margareta",
    "resource": 478393,
    "start": "2017-09-03 07:20:00",
    "searchText": "Margareta Alfredsson 193005252428",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  },
  {
    "actionTypeId": 1,
    "areaId": 261,
    "end": "2017-09-03 07:40:00",
    "endDate": "0001-01-01T00:00:00",
    "id": 8146090,
    "isCustomerPaused": false,
    "frequencyInWeeks": 1,
    "text": "Bengtsson, Sif Linnea",
    "resource": 478396,
    "start": "2017-09-03 06:40:00",
    "searchText": "Sif Linnea Bengtsson 192705134845",
    "cssClass": "event_visit",
    "isDayAction": false,
    "pcComp": 0,
    "pcNotComp": 0
  }
]
Comment posted by Dan Letecky [DayPilot]
7 years ago.

Thanks. You are right, it looks like a timing problem.

There is now a new version available that fixes the [events] attribute (8.4.2977). There are also a couple of minor improvements. Could you please give it a try?

If the problem persists, could you please check dp.resources in the browser console when the rendering fails - i.e. is it empty or does it hold the right data?

Comment posted by Martin Säfsten
7 years ago.

Hey, apologies for the delay.

So the rendering works on both scenario, so that part works great.

I dont get double events when a new one is added anymore, but if I move, resize or change the data of an event, it renders it twice.

So whatever you did, seems to have worked on add event, but not update event.

Comment posted by Dan Letecky [DayPilot]
7 years ago.

Thanks for the update.

I understand that scenario #2 (direct API) works fine including event updates (move, resize). The problem is just with [events] attribute. Is that correct?

Comment posted by Martin Säfsten
7 years ago.

Yes, exactly.

Using direct update via control.events.list and control.resources, and one control.update() seems to do what I want. doing event attribute, and a control.update() does the same event rendering each, rendering everything twice.

Just a thought from a fellow programmer. Maybe disable event updating from control.update() if events attribute is used?

This question is more than 1 months old and has been closed. Please create a new question if you have anything to add.