fullCalendar - Zbyt późne wczytanie danych do tablicy

0

Używam pluginu fullCalendar http://fullcalendar.io/docs/ w mojej aplikacji ale natchnąłem się na problem z którym nie potrafię sobie poradzić. Mianowicie przed wczytaniem kalendarza na stronę wraz z eventami które tworzą mi się automatycznie, pobieram dane z bazy danych przez kontroler do którego prowadzi funkcja loadData. Wszystko działa pięknie tylko problem polega na tym, że jak pierwszy raz ładuję stronę na której jest kalendarz to funkcja eventRender zachowuje się tak jakby tablica myData była pusta. Tablica pusta nie jest - wg. debuggera w przeglądarce. Jeżeli zmienię tydzień w kalendarzu na następny dopiero wtedy załadują mi się eventy z tablicy myData, czyli można powiedzieć dopiero jak za drugim razem uruchomi się funkcja eventRender. Nie wiem czemu jak program przechodzi pierwszy raz przez funkcje eventRender to zachowuje się jakby tablica myData była pusta. Nawet jak włączę stronę z kalendarzem, czyli w tym momencie nie wczytały mi się te dane z tablicy do kalendarza(nie mam na nich stylu css) to jak nacisnę event który jest w tabeli to już program wykrywa że się tam znajduję i np. nie mogę na nim wykonać akcji. Próbowałem już dziesiątki rzeczy i nie wpadłem na nic co by pozwoliło mi to naprawić :(. Będę wdzięczny za pomoc!
P.S $('#calendar').fullCalendar('refetchEvents');- użycie tej funkcji mnie nie satysfakcjonuje bo długo się to wszystko będzie wczytywać!

@section scripts{
    <link href="~/Content/bootstrap.css" rel="stylesheet" />
    <script src="~/Scripts/moment.js"></script>
    <script src="~/Scripts/moment-timezone.js"></script>
    <link href="~/Content/fullcalendar.css" rel="stylesheet" />
    <script src="~/Scripts/fullcalendar.js"></script>
 
    <script id="jsbin-javascript">
 
        var clickedEvents = [];
 
        var myData = [];
 
        var myEvents = [];
        
         //Pobieram dane z kontrolera w formacie string
         var loadData = function () {
                $.get("/BookVisit/TakeBookedEvents/", function (data, status) {
                    for (var i = 0; i <= data.length; i++) {
                        myData.push(data[i].loginUser);
                    }
                });
            };
 
        //Automatycznie tworzę eventy do kalendarza
        var timeCursor = moment(); 
        var endtime = +moment().add(5, 'days');
        while (+timeCursor < endtime) {
            var start = +timeCursor;
            timeCursor = timeCursor.add(15, 'minutes');
            var end = +timeCursor;
            myEvents.push({ start: start, end: end });
        }
 
        var eventRender = function (event, element) {
            $(element).html(moment(event.start).format('h:mm'));
            
            //podczas renderowania eventów sprawdzam czy któryś z nich znajduje się w tablicy myData jeżeli tak dodaję klasę css.
            if (myData.indexOf(event.start.format()) >= 0)
            {
                $(element).addClass('already-clicked');
                return element;
            }
            else {
 
                return element;
            }
        };
 
        $(document).ready(function () {
 
            loadData();
 
            $('#calendar').fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'agendaWeek,agendaDay'
                },
                defaultView: 'agendaWeek',
                editable: false,
                allDaySlot: false,
                selectable: true,
                events: myEvents,
                eventColor: '#33CC33',
                timezone: 'UTC',
                eventRender: eventRender,
            });
        });
    </script>
}

W załączniku jeszcze jest zdjęcie

1

pobierasz dane jquerowskim $.get czyli de facto technologia AJAX, skrot ten mozna rozwinac do Asynchronous JavaScript and XML. Asynchronous znaczy Asynchroniczny, czyli nie dzialajacy jednoczesnie, czyli w tle.

Po tym krotkim wstepie pseudokod wyglada tak:

  1. wczytaj strone
  2. odpal $(document).ready(function () {
  3. odpal funckje pobierajaca dane
  4. zaladuj skrypt kalendarza korzystajac z danych w mydata

ale problem jest w tym ze 3 jest asynchroniczna i Slinik JS nie czeka az ta fuknkcja sie zakonczy tylko od razu odpala punkt 4, a co za tym idzie gdy 4 sie laduje to danych tam jeszcze nie ma., pojawaiaja sie ulamek sekundy pozniej gdy serwer zwroci dane.

Dlatego w funkcji zwrotnej w ktorej wypelniasz tablice na koncu odpal funkcje ktora wygeneruje kalendarz. a inicjalizacje skrypotu kalendarza przenies do tej funckji. wtedy skrypt zaladuje sie jak dane beda uzupelnione.

czyli np. tak:

        var clickedEvents = [];
 
        var myData = [];
 
        var myEvents = [];
 
         //Pobieram dane z kontrolera w formacie string
         var loadData = function () {
                $.get("/BookVisit/TakeBookedEvents/", function (data, status) {
                    for (var i = 0; i <= data.length; i++) {
                        myData.push(data[i].loginUser);
                        
                    }
                     zaladujKalendarz();
                });
            };
 
        //Automatycznie tworzę eventy do kalendarza
        var timeCursor = moment(); 
        var endtime = +moment().add(5, 'days');
        while (+timeCursor < endtime) {
            var start = +timeCursor;
            timeCursor = timeCursor.add(15, 'minutes');
            var end = +timeCursor;
            myEvents.push({ start: start, end: end });
        }
 
        var eventRender = function (event, element) {
            $(element).html(moment(event.start).format('h:mm'));
 
            //podczas renderowania eventów sprawdzam czy któryś z nich znajduje się w tablicy myData jeżeli tak dodaję klasę css.
            if (myData.indexOf(event.start.format()) >= 0)
            {
                $(element).addClass('already-clicked');
                return element;
            }
            else {
 
                return element;
            }
        };
 
        $(document).ready(function () {
 
            loadData();
 
            
        });

function zaladujKalendarz(){

$('#calendar').fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'agendaWeek,agendaDay'
                },
                defaultView: 'agendaWeek',
                editable: false,
                allDaySlot: false,
                selectable: true,
                events: myEvents,
                eventColor: '#33CC33',
                timezone: 'UTC',
                eventRender: eventRender,
            });
}
0

Płakać mi się chcę nad sobą że zmarnowałem na to tyle czasu a nawet nie doczytałem że to funkcja asynchroniczna...
Próbowałem zrobić Twoim sposobem ale cały czas nie wczytują mi się dane z tablicy do kalendarza przed samym kalendarzem :/ Nie wiem czemu.

Próbowałem też tak:

 var clickedEvents = [];
 
        var myData = [];
 
        var myEvents = [];
 
         //Pobieram dane z kontrolera w formacie string
         var loadData = function () {
                
                $.ajax({
                async: false,
                type: 'get',
                url: '/BookVisit/TakeBookedEvents/',
                dataType: "json",
                contentType: false,

                success: function (data) {
                    for (var i = 0; i <= data.length; i++) {
                        myData.push(data[i].loginUser);
                    }
                },
                complete: function (data) {
                    zaladujKalendarz()
                },
            });
            };
 
        //Automatycznie tworzę eventy do kalendarza
        var timeCursor = moment(); 
        var endtime = +moment().add(5, 'days');
        while (+timeCursor < endtime) {
            var start = +timeCursor;
            timeCursor = timeCursor.add(15, 'minutes');
            var end = +timeCursor;
            myEvents.push({ start: start, end: end });
        }
 
        var eventRender = function (event, element) {
            $(element).html(moment(event.start).format('h:mm'));
 
            //podczas renderowania eventów sprawdzam czy któryś z nich znajduje się w tablicy myData jeżeli tak dodaję klasę css.
            if (myData.indexOf(event.start.format()) >= 0)
            {
                $(element).addClass('already-clicked');
                return element;
            }
            else {
 
                return element;
            }
        };
 
        $(document).ready(function () {
 
            loadData();
 
 
        });
 
function zaladujKalendarz(){
 
$('#calendar').fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'agendaWeek,agendaDay'
                },
                defaultView: 'agendaWeek',
                editable: false,
                allDaySlot: false,
                selectable: true,
                events: myEvents,
                eventColor: '#33CC33',
                timezone: 'UTC',
                eventRender: eventRender,
            });
}

Przeczytałem że complete wykonuje się po success - w tym moim przykładzie niby pętla się wykonuje ale na tym się kończy, nic dalej się nie dzieje. Nie ładuje się kalendarz.

EDIT: A jak usunę complete i wyświetlenie kalendarza zrobię w ten sposób, to kalendarz mi się wyświetli ale również nie widzę na nim eventów ze zmienionym stylem z tablicy. Dopiero po naciśnięciu na następny tydzień działa - czyli tak jak było :(

success: function (data) {
                    var val= data;
  
                    for (var i = 0; i <= jol.length; i++) {
                        myData.push(val[i].loginUser);

                        if(i <= val.length)
                        {
                            loadCalendar();
                        }
                    }
                    
                },
 

Tak wygląda mój kontroler

 
public JsonResult TakeBookedEvents()
        {
            List<VisitDetails> details = dd.GetAll;
            IEnumerable<VisitDetails> deta = details.AsEnumerable();
            var data = deta.Select(s => new
                {
                    loginUser = s.LoginUser
                });
            var array = data.ToArray();
            
            return Json(array, JsonRequestBehavior.AllowGet);
        }

@szalonyfacet rzuć okiem proszę na ten mój kod i/lub powiedz co w Twoim można zmienić żeby to wczytało dane z tablicy do eventów przed wczytaniem fullCalendara. Dzięki za pomoc.

1

ehh, po co rozdzielasz to na complete i success. Czy gdy serwer zwroci blad to wciaz chcesz wygenerowac pusty kalendarz czy zwrocic info ze cos poszlo nie tak??

a teraz dlaczego nie dziala. complete odpala sie po success, ale nie czeka na jego wykonanie. Czyli bedzie odpalony kod w success a zaraz po nim kod w complete, co oznacza petla z success jeszcze leci jak kalendarz sie uruchamia.

odpal funkcje load Calendar ZARAZ ZA petla wypelniajaca myData, a wtedy kalendarz poczeka az myData bedzie pelne.

jak np. tu//jsbin.com/vasehobuwe/edit?html,console,output

0

Powiem szczerze że dalej nie chce się to poprawnie włączyć. Jeżeli wywołam funkcję zaladujKalendarz(); zaraz po pętli for (tak jak w Twoim kodzie), to nawet nie wczytuje mi się kalendarz. Wydaje mi się że może trzeba to jawnie zakomunikować funkcji get że dopiero jak skończy się wykonywać żeby przeszła do załadowania kalendarza.

0

masz na dole mojego postu kod, ktory pobiera jsona, wykonuje petle i dopiero wtedy laduje kalendarz. kalendarz sie laduje. wiec dziala. a co do zakomunikowania, to jak wczytasz sie w struklture funkcje get to zobaczysz, ze drugi parametr jest funkcja zwrtona, czyli callbackiem, i odpala sie jak zadanie wykonanie sie pomyslnie.

0

W końcu działa. Dodałem tą linijkę z Twojego kodu w jsbin

if (data[i] != undefined) {
...
}

Dzięki bardzo za pomoc z tym!

Mam jeszcze takie pytanie czy czas około 2s od momentu naciśnięcia przez użytkownika przycisku do przejścia na stronę i poczekanie na załadowanie pluginu to długo?

1 użytkowników online, w tym zalogowanych: 0, gości: 1