Our pure JavaScript Scheduler component


Post by kbraiden »

I have been banging my head against a wall trying to get something working that I thought would be basic functionality without any luck.

I have a vue-2/nuxt implementation of scheduler-pro trial. Data is retrieved directly from a SQL backend, and we have this working successfully. What I was hoping would be easy to do is to periodically refresh the data from our database but this is frustratingly more complicated than I would like it to be. Is this not a common scenario in any of the reactive frameworks? Or perhaps I am missing the page that explains this?

Here is how I am attempting to get the data to refresh.

In the component VUE page, in the mounted lifecycle hook I have the following code:

 async mounted() {
    this.thisProject = this.$refs.schedulerpro.project
    setInterval(() => {
      if (this.thisProject) {
       console.log('timer initiated')
        this.thisProject.sync()
      }
    }, 10000)
  }

In the config file:

const project = (window.project = new ProjectModel({
  autoLoad: true,
  syncDataOnLoad: true,
  eventStore : {
        syncDataOnLoad : true
    },
  transport: {
    load: {
      url: '/api/train-schedule'
    },
    sync: {
      url: '/api/train-schedule',
      method: 'post'
    }
  }
}))

const schedulerproConfig = {
  project,
  ...

I don't think this is the problem (see below) but here is the api to call the data. I am mainly including this to show the "got to sync" console message that I reference below. If there is confusion on what we are doing here we call a service that calls SPs from our DB that return our data, we have one SP for each type of data (event, resource, dependency, assignment):

router.post('/train-schedule/:type?', async (req, res) => {
console.log('got to sync')
  const url = 'redacted'
  const body = {
    queryTimeout: 100,
    inputParameters: [],
    outputParameters: []
  }
  const type = req.params?.type?.toLowerCase()

  const sps = {
    resources: 'TimelineResources_SelectData',
    events: 'TimelineEvents_SelectData',
    assignments: 'TimelineAssignments_SelectData',
    dependencies: 'TimelineDependencies_SelectData'
  }

  if (type && sps[type]) {
    const { data } = await axios.post(url, {
      ...body,
      storedProcedureName: sps[type]
    })

return res.json(data.ResultSets.Table1)
  }

  const resources = axios.post(url, {
    ...body,
    storedProcedureName: 'TimelineResources_SelectData'
  })

  const dependencies = axios.post(url, {
    ...body,
    storedProcedureName: 'TimelineDependencies_SelectData'
  })

  const assignments = axios.post(url, {
    ...body,
    storedProcedureName: 'TimelineAssignments_SelectData'
  })

  const events = axios.post(url, {
    ...body,
    storedProcedureName: 'TimelineEvents_SelectData'
  })

  const response = await Promise.all([
    resources,
    dependencies,
    assignments,
    events
  ]).catch()

  const [Resources, Dependencies, Assignments, Events] = response.map(
    item => item.data
  )

  res.json({
    resources: {
      rows: Resources.ResultSets.Table1
    },
    dependencies: {
      rows: Dependencies.ResultSets.Table1
    },
    assignments: {
      rows: Assignments.ResultSets.Table1
    },
    events: {
      rows: Events.ResultSets.Table1
    }
  })
})

This initially loads the data properly when component is mounted, I get one instance of the API message "Got To Sync" and when the interval timer fires I do see the console.message "timer initiated". But there is no network call to get the data via sync() and the console.message "got to sync" does not fire again (which leads me to believe that sync isn't actually being called when the timer fires). If I do nothing I continue to see the "timer initiated" message fire at every interval - so timer is still working. But no changes in the UI when I make changes on the backend, no network calls are kicked off and sync is never called...

UNTIL I manually adjust the length of any event, or presumably make any change to an event. Then the sync API fires at the given timer interval, I see "got to sync" message and the data starts to flow as expected. I can make changes in my database and the next time the timer fires I see the update values reflected in the UI - everything is working how I would expect. That is until I refresh the page, then the problem starts over again.

Is there something I am missing? How do I kick start the sync to work right off the bat? I have syncDataOnLoad set to true which is what I hoped would fix the issue but no luck.

Thanks for any help here.


Post by mats »

Sync is our terminology for "save" (poor naming perhaps). So, if there are no changes made, no sync action will happen. It sounds like you want to trigger load instead, try replacing with:

async mounted() {
    this.thisProject = this.$refs.schedulerpro.project
    setInterval(() => {
      if (this.thisProject) {
       console.log('timer initiated')
        this.thisProject.load()
      }
    }, 10000)
  }

Post by kbraiden »

Thanks Mats, I thought this was the solution as well for a while because indeed this did go and fetch the data every time (including on load) but it never actually updated the UI. Looking through the forums I saw reference to project.refresh() so I thought this might work:

async mounted() {
    this.thisProject = this.$refs.schedulerpro.project

setInterval(() => {
  if (this.thisProject) {
    console.log('timer initiated')
    this.thisProject.load().then(() => {
      console.log(this.thisProject)
      this.$refs.schedulerpro.project.refresh()
    })
  }
}, 10000)
  }
  

But this console.log(this.thisProject) didn't have a refresh() function.

Exception: TypeError: _this.thisProject.refresh is not a function at eval (webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./components/Train/gantt.vue?vue&type=script&lang=js&:61:39)
message: "_this.thisProject.refresh is not a function"
stack: "TypeError: _this.thisProject.refresh is not a function\n    at eval (webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./components/Train/gantt.vue?vue&type=script&lang=js&:61:39)"
syncproblem.png
syncproblem.png (114.11 KiB) Viewed 831 times

So I guess I have 2 questions, should load be refreshing the UI automatically and if not and I need to call the refresh method, how do I do that? Do I need to build that refresh function?

Thanks again for your quick reponses.


Post by alex.l »

Hi kbraiden,

it never actually updated the UI

That sounds like a problem. There is no need to do any extra refresh for the UI after load call. It will work same if you, as example, disabled autoLoad and called initial load manually with schedulerPro.project.load(). You will see all data loaded, so it will be working same way for 2nd call.

Please check if data structure for 1st and 2nd load call is the same. If so, it might be good to post your runnable code here, so we will be able to check your all setup and find what's wrong.

All the best,
Alex


Post by johan.isaksson »

Hi,

We could definitely improve docs (and possibly also the sync() function) in this area. Have opened ticket on it which you can track here: https://github.com/bryntum/support/issues/4575

Meanwhile, Mats/Alex suggestions sounds like the way to go for now.

Best regards,
Johan Isaksson

Post by kbraiden »

Yes, can confirm this is now working for me. It was the syncDataOnLoad: true that was getting me (it should be set to false). Thanks for the guidance here. For anyone else looking into this I have posted the combination of settings that worked for us at the bottom of this post.

Outstanding issues

  • There is a flicker on every load. Is this a problem that has been solved? Something like fullRowRefresh : false but it doesn't seem to work to solve this issue?

  • Is there a way to suppress the loading popup?
    edit - this is the fix here: loadMask: null on the scheduler.

  • Is syncDataOnLoad required for every store type? i.e.

 
 resourceStore: {
    syncDataOnLoad: false
  },
   assignmentStore: {
    syncDataOnLoad: false
  },
  etc...
  

Or is there a way to assign that globally so that all stores get that property?

Working config:

const project = (window.project = new ProjectModel({
  autoLoad: true,
  eventStore: {
    syncDataOnLoad: false
  },
  transport: {
    load: {
      url: '/api/train-schedule'
    },
    sync: {
      url: '/api/train-schedule',
      method: 'post'
    }
  }
}))

...and Vue file Mounted hook:

async mounted() {
    this.thisProject = this.$refs.schedulerpro.project
    setInterval(() => {
      if (this.thisProject) {
        this.thisProject.load()
      }
    }, 10000)
  }
  

Post by johan.isaksson »

Hi,

Without knowing all details I would actually have expected you to use syncDataOnLoad: true. That way only the differences from the dataset that you are loading would be applied. There has been some fixes for it in recent patch releases, which version are you on?

There is currently no way of toggling it for all stores at once, but it has been discussed internally and is something we will add. We have a ticket open on it already: https://github.com/bryntum/support/issues/4534

Regarding the flickering it is hard to guess whats going on. Perhaps there is some CSS transition coming into play? You could check that using the Animations panel in dev tools. Or you could try disabling initial animation (https://bryntum.com/docs/scheduler/api/Scheduler/view/Scheduler#config-useInitialAnimation) & event animations (https://bryntum.com/docs/scheduler/api/Scheduler/view/Scheduler#config-enableEventAnimations) to rule things out.

Best regards,
Johan Isaksson

Post by kbraiden »

Thanks Johan, I would have expected the opposite as well. Could that have anything to do with the flicker? Is it perhaps reloading all of the events instead of updating the changed values? That is what it feels like. I would expect in a reactive environment like Vue that the values would change in the store and then reflect in the events without a redraw but what I am seeing is that all of the events are disappearing and then re-added (as well as the columns in the Grid). I turned off the animations via those parameters and still seeing the issue.

This is how we are setup at the moment, tell me if there is another version that we should be on that might change something.

"@bryntum/schedulerpro": "npm:@bryntum/schedulerpro-trial@5.0.2",
"@bryntum/schedulerpro-vue": "5.0.2",


Post by johan.isaksson »

With syncDataOnLoad: false it will be replacing the full dataset yes, with it true it would only update what has changed.

There is a newer version that you could try, 5.0.3. If it still does not work with syncDataOnLoad: true it would be great if you could supply a failing dataset for us to check, intention is for it to work in your scenario.

Best regards,
Johan Isaksson

Post by jhankar »

Hi Johan, I'm using syncDataOnLoad: true, and if I update any string type data in backend it is getting reflected in UI correctly. However, when I update any datetime field, UI is not getting updated until I refresh the browser.


Post Reply