Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Mike Taylor 155 posts 353 karma points
    Aug 01, 2019 @ 20:40
    Mike Taylor
    0

    Angular errors when setting defaultDate on <umb-flatpickr> date pickers inside an ng-repeat loop

    Hi there,

    I have a personal side-project for a holiday lettings website, and I'm developing a custom property editor to manage occupancy and rates.

    The intention is to be able to specify a series of items containing a start date and a set of rates (depending on the number of people occupying the property).

    Here's a design concept for the property editor:

    Design concept for the rates editor

    This requires a date picker to be inside an ng-repeat loop. I've almost got it working, but I'm getting a ton of Angular errors when I'm setting the initial values of the date pickers from the data model.

    A bit of background:

    The initial date of an umb-flatpickr element is set via the 'defaultDate' property of an options object. When you only have a single date picker, you can just define the options object in your controller, and set the defaultDate there. However, when it's inside an ng-repeat, it needs to be set for each iteration.

    In my controller, I'm doing this:

    $scope.defaultDateTimeConfig = {
                weekNumbers: true,
                altInput: true,
                altFormat: "F j, Y",
                dateFormat: "Y-m-d",
                onChange: function(selectedDates, dateStr, instance) {
    
                    var rootIndex = instance.element.parentElement.parentElement.getAttribute('data-index'); // hacky, much?
                    $scope.model.value.rates[rootIndex].fromDate = dateStr;
    
                    // set current year to this one
                    var thisYear = new Date(dateStr).getFullYear();
                    if (thisYear != $scope.showYear) {
                        $scope.showYear = thisYear;
                    }
    
                    calculateAllYears();
    
                }
            };
    
            $scope.dateOptions = function(dateStr) {
                return angular.extend({ defaultDate: new Date(dateStr) }, $scope.defaultDateTimeConfig);
            };
    

    So, I'm defining an object for all the common settings, and then using a function to extend that object with a defaultDate property which takes a value passed to the function.

    In my view, I'm doing this:

       <div ng-repeat="rate in model.value.rates | startsInYear: showYear" class="clr-rates">
    
            <hr />
    
            <p>fromDate</p>
            <p><umb-flatpickr options="dateOptions(rate.fromDate)" data-index="{{ rate.index }}"></umb-flatpickr></p>
    
    ...
    

    So, the 'options' attribute is calling the dateOptions function and passing in the value from the data model.

    This all works. Yay me.

    However.... in Chrome dev tools, I'm seeing an absolute ton of Angular errors:

    enter image description here

    My Angular is rudimentary, so I'm not totally sure what's going on here, but I suspect that having the default options and/or function in $scope is creating a load of unnecessary watches?

    I'm hoping that someone with more Angular knowledge than me will know a better / correct way to do this that doesn't spit out hundreds of console errors.

    All the code's on Github here - any pointers would be much appreciated!

    (Side note: getting the data model to update properly when you change the date in the picker was 'fun' - that's working too, but it's also super-hacky! Be nice!)

  • Matt Brailsford 4125 posts 22223 karma points MVP 9x c-trib
    Aug 01, 2019 @ 21:38
    Matt Brailsford
    0

    According to this https://docs.angularjs.org/error/$rootScope/infdig it can happen when you bind your ng-repeat to a dynamic collection and whilst your collection isn't dynamic (rates) you do have a change handler which alters it so I think you have a bit of an infinite loop.

    1. Repeat over rates
    2. Render date picker binding options causing a change event
    3. Change triggers onchange which modifies a value inside rates collection
    4. Angular sees the change and thinks it needs to re-render the list
    5. Rinse and repeat

    This is my guess anyways

    Matt

  • Mike Taylor 155 posts 353 karma points
    Aug 01, 2019 @ 22:33
    Mike Taylor
    100

    OK - I think I have it sorted (but still a bit nasty).

    I'm no longer sending the defaultDate setting to the function. Instead, I'm adding a value attribute to the view element:

    <umb-flatpickr options="defaultDateTimeConfig" data-index="{{ rate.index }}" value="{{ rate.fromDate }}"></umb-flatpickr>
    

    And then grabbing this from the onReady event in the defaultDateTimeConfig options in the controller:

    $scope.defaultDateTimeConfig = {
                weekNumbers: true,
                altInput: true,
                altFormat: "F j, Y",
                dateFormat: "Y-m-d",
                onChange: function(selectedDates, dateStr, instance) {
    
                    var rootIndex = instance.element.parentElement.parentElement.getAttribute('data-index'); // hacky, much?
                    $scope.model.value.rates[rootIndex].fromDate = dateStr;
    
                    // set current year to this one
                    var thisYear = new Date(dateStr).getFullYear();
                    if (thisYear != $scope.showYear) {
                        $scope.showYear = thisYear;
                    }
    
                    calculateAllYears();
    
                },
                onReady: function() {
                    this.setDate(new Date(this.element.parentElement.parentElement.getAttribute('value')));
                }
            };
    

    No console errors, and everything seems to be updating. Phew.

    Thanks for looking at it Matt (and Nik).

    Time for wine and sleep.

Please Sign in or register to post replies

Write your reply to:

Draft