Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: newToast.scope.init is not a function #109

Closed
dakolech opened this issue Jun 11, 2015 · 43 comments
Closed

TypeError: newToast.scope.init is not a function #109

dakolech opened this issue Jun 11, 2015 · 43 comments

Comments

@dakolech
Copy link

I'm using angular-toastr in my project and i've got this error, when first toastr appears.

TypeError: newToast.scope.init is not a function
  File http://page.com/js/vendor.js line 73525 col 15 in _notify/</</<
  File http://page.com/js/vendor.js line 22503 col 27 in processQueue
  File http://page.com/js/vendor.js line 22519 col 27 in scheduleProcessQueue/<
  File http://page.coml/js/vendor.js line 14151 col 7 in completeOutstandingRequest
  File http://page.com/js/vendor.js line 14539 col 7 in Browser/self.defer/timeoutId<

Unfortunately, this error appear always on server (for first toastr) and only one on my local machine.

This is my config:

angular.extend(toastrConfig, {
  closeButton: false,
  iconClasses: {
    info: 'toast-message',
    warning: 'toast-cart'
  },
  templates: {
    toast: 'templates/common/services/alersts_service/alertsServ.html'
  }
});

I'm inlcuding angular-toastr.tpls.js script in my html files.

@Foxandxss
Copy link
Owner

Weird, but if you're using a different template, don't use the tpls version, but still it shouldn't fail.

@dakolech
Copy link
Author

i tried many capabilities, but none works

@Foxandxss
Copy link
Owner

I see. I would need a reproduction (plunker for example) to be able to test that myself. Or better you could look into the problem and send a PR :)

@dakolech
Copy link
Author

i have something like this: http://plnkr.co/edit/RJaDKSJZk3hSSOrfQ9nv

@Foxandxss
Copy link
Owner

I need a working reproduction, that plunker outputs a lot of errors.

@dakolech
Copy link
Author

there it is, working

@Foxandxss
Copy link
Owner

I see it. Really weird. Will investigate.

@Foxandxss
Copy link
Owner

Give me some information please. How are you loading the template in your solution? It is a .html file you fetch, it is on templateCache...

I can't reproduce it on my local playground.

@Foxandxss
Copy link
Owner

Ok, I see where the problem is, now for a fix.

@Foxandxss
Copy link
Owner

I created an issue at the angular repo, I am not sure if the problem is mine or not.

@Foxandxss
Copy link
Owner

So the issue is mine, I will think about a way of fixing it. In the meantime I highly recommend you to cache all your templates before booting the app. It is a common thing to do and will fix this issue for now.

@Foxandxss
Copy link
Owner

I tried to fix this but there are some limitations in angular. In the time being I highly recommend you (if you still use this library) to put your template in $templateCache if possible.

@dakolech
Copy link
Author

dakolech commented Aug 6, 2015

I did it. Hope it will be fixed in the future.

@Foxandxss
Copy link
Owner

I am having some talks with a few folks and they all think the same. The end user has to cache their templates beforehand. I agree with them, it is a sane thing todo.

The problem is that angular itself doesn't have some facilites to allow $animate to wait until the directive is compiled (with its children) and I would need to break half library to hack it.

So I am sorry, not a supported feature. Will add a note in the readme.

@dcardosods
Copy link

Reading this issue and the note in the README I understand that this the problem would happen only with custom templates, but I'm using the provided template, i.e. angular-toastr.tpls.js, and still get this error.

@Foxandxss
Copy link
Owner

@dcardosods If you manage to reproduce it in a plunker, please create a new issue so I can take a peek.

@TorosyanV
Copy link

Same problem.

@TorosyanV
Copy link

I think i will use https://www.nuget.org/packages/toastr. It is working.

@Foxandxss
Copy link
Owner

That will work for sure (if you don't mind the extra dep).

It is not like I don't want to fix the error, but I can't spend dozen of hours trying everything to find your issue. I really need a way to know how you managed to get the error so I can see it myself, create a test and fix it. I can't do it blind.

@dcardosods
Copy link

@Foxandxss I couldn't manage to isolate the problem. Even on my application it's happening intermittently.

@artdias90
Copy link

Same problem on angular 1.4

@Foxandxss UPDATE: Tried using tpls version and it worked.

@Foxandxss
Copy link
Owner

@artdias90 Would you be kind to create a reproduction? #129 Thanks.

@Foxandxss
Copy link
Owner

If the error doesn't show up, what issue are you seeing in that case?

@tmundt
Copy link

tmundt commented May 11, 2016

I would like to use toastr.clear without errors.
Am 11.05.2016 16:16 schrieb "Jesús Rodríguez" [email protected]:

If the error doesn't show up, what issue are you seeing in that case?


You are receiving this because you commented.
Reply to this email directly or view it on GitHub

@Foxandxss
Copy link
Owner

I understand, this issue is very problematic but hard to fix. What error do you get @tmundt ?

I will give time to this issue next week and try to release a new version.

@bjorn-ali-goransson
Copy link

Just got this, just took over a project using ng-toastr ... I don't have the background on this, but I see the toast is working, and here a certain scope.init is expected but I don't see where it would be defined.

Here it's expected, and my fix is inside:

        newToast.open.promise.then(function() {
            _createOrGetContainer(options).then(function() {
                newToast.isOpened = true;
                if (options.newestOnTop) {
                    $animate.enter(newToast.el, container).then(function() {
                        if (newToast.scope.init) newToast.scope.init();
                    });
                } else {
                    var sibling = container[0].lastChild ? angular.element(container[0].lastChild) : null;
                    $animate.enter(newToast.el, container, sibling).then(function() {
                        if (newToast.scope.init) newToast.scope.init();
                    });
                }
            });
        });

@Foxandxss
Copy link
Owner

I thought about that, but if there is some "legit" toast in there that wants to show up and the .init() never fires, the toast will stick there for ever.

I pushed a new update to master that removes replace: true which led to a lot of timing issues.

Since I don't have a reproduction I can try, I would love other people having the issue to check it out and see if that new master version does the job.

@bjorn-ali-goransson
Copy link

Can I mail you a link. Don't want to post it here openly

@Foxandxss
Copy link
Owner

Of course

@mjknight50
Copy link

I ran into the same issue and used the fix from Björn to make it work.

@kobvel
Copy link

kobvel commented Jul 21, 2016

still occurs in the 1.7.0

@Foxandxss
Copy link
Owner

I will release a new version without replace: true for this weekend and hopefully fix this.

@kobvel
Copy link

kobvel commented Jul 21, 2016

many thanks

@iberodev
Copy link

What's the status on this issue?

I am having the same problem with angular 1.5.8 and with the latest angular-toastr 2.1.1

I am using $templateCache for all my views. Not sure why this would make any difference though.

I am also wrapping all the toastr functionality in a custom injectable service (see below). Whether I inject or not the toastrConfig into that service or not to configure things like the target (because sometimes I want validation errors to be displayed in a specific <div> or with full width rather than by default) every time one of this service functions is called, it does not make any difference.

The error happens always when a toastr is already on screen (i.e: if I call another function before it disappears) even if I clear all the toastr before invoking the new one.

/// <reference path="../../typings/angular-toastr/angular-toastr.d.ts" />

namespace app.services {
    "use strict";

    enum MessageType {
        ERROR,
        WARNING,
        INFO,
        SUCCESS,
        VALIDATION_ERROR
    }

    export interface IToastService {
        showSuccessMessage(message: string, title?: string): void;
        showWarningMessage(message: string, title?: string): void;
        showErrorMessage(message: string, title?: string): void;
        showInfoMessage(message: string, title?: string): void;
        showValidationError(message: string, title?: string): void;
        showValidationErrors(validationErrors: blocks.ISpeediCargoApiValidationErrors, title?: string): void;
    }

    class ToastService implements IToastService {
        constructor(
            private $anchorScroll: ng.IAnchorScrollService,
            private $location: ng.ILocationService,
            private toastr: ng.toastr.IToastrService,
            private toastrConfig: ng.toastr.IToastrConfig) {
        }

        public showSuccessMessage(message: string, title?: string): void {
            this.showMessage(MessageType.SUCCESS, message, title);
        }

        public showWarningMessage(message: string, title?: string): void {
            this.showMessage(MessageType.WARNING, message, title);
        }

        public showErrorMessage(message: string, title: string): void {
            this.showMessage(MessageType.ERROR, message, title);
        }

        public showInfoMessage(message: string, title: string): void {
            this.showMessage(MessageType.INFO, message, title);
        }

        public showValidationErrors(validationErrors: blocks.ISpeediCargoApiValidationErrors, title?: string): void {
            let message = "<ul>";
            let errors: blocks.ISpeediCargoApiValidationError[] = validationErrors.errors;

            for (let err of errors) {
                message += "<li>" + err.message + "</li>";
            }
            message += "</ul>";

            this.showValidationError(message, title);
        }

        public showValidationError(message: string, title?: string): void {
            this.toastr.clear();
            if (title === undefined || title === null) {
                title = this.getDefaultTitle(MessageType.VALIDATION_ERROR);
            }

            this.toastrConfig.target = ".speedi-error";
            this.toastrConfig.positionClass = "toast-top-full-width";
            this.toastrConfig.preventOpenDuplicates = false;
            this.toastr.error(message, title);
            this.scrollToTop();
        }

        private showMessage(type: MessageType, message: string, title?: string) {
            if (title === undefined || title === null) {
                title = this.getDefaultTitle(type);
            }
            this.toastrConfig.target = "body";
            this.toastrConfig.positionClass = "toast-top-center";
            this.toastrConfig.timeOut = 5000;
            this.toastrConfig.extendedTimeOut = 1000;
            this.toastrConfig.preventOpenDuplicates = false;

            if (type === MessageType.ERROR) {
                this.toastr.error(message, title);
            } else if (type === MessageType.INFO) {
                this.toastr.info(message, title);
            } else if (type === MessageType.WARNING) {
                this.toastr.warning(message, title);
            } else if (type === MessageType.SUCCESS) {
                this.toastr.success(message, title);
            } else {
                throw "type " + type + " unknown";
            }
        }

        private getDefaultTitle(type: MessageType): string {
            if (type === MessageType.ERROR) {
                return "Error";
            } else if (type === MessageType.INFO) {
                return "Information";
            } else if (type === MessageType.WARNING) {
                return "Warning";
            } else if (type === MessageType.SUCCESS) {
                return "Success!";
            } else if (type === MessageType.VALIDATION_ERROR) {
                return "Please correct the following errors:";
            }
            throw "type " + type + " unknown";
        }

        private scrollToTop(): void {
            this.$location.hash("top");
            this.$anchorScroll();
        }
    }

    factory.$inject = [
        "$anchorScroll",
        "$location",
        "toastr",
        "toastrConfig"
    ];

    function factory(
        $anchorScroll: ng.IAnchorScrollService,
        $location: ng.ILocationService,
        toastr: ng.toastr.IToastrService,
        toastrConfig: ng.toastr.IToastrConfig): IToastService {
        return new ToastService($anchorScroll, $location, toastr, toastrConfig);
    }

    angular
        .module("app.services")
        .factory("app.services.ToastService", factory);
}

@Foxandxss
Copy link
Owner

It should be fixed, but apparently not. Would you be able to reproduce this in a plunker or a little project at github? I would like to try.

@bo01ean
Copy link

bo01ean commented Nov 4, 2016

I have this issue only when I minify.

I speculate that the async animate functionality loses context somehow?

I might try to refactor and not use scope and see if that helps :)

Cool library!

@commonpike
Copy link

commonpike commented Jan 4, 2017

"if you're using a different template, don't use the tpls version" .. how can i prevent using the tpls version ?

[edit - sorry - that was obvious, i was including it in the html, just hadn't noticed.]

@commonpike
Copy link

i had this intermittently, so it seemed like a timing option. wrapping it in a timeout, which gives it a single cycle more, seems to solve it here .. sofar ..

$animate.enter(newToast.el, container).then(function() {
     $timeout(function() {
       if (newToast.scope.init) newToast.scope.init();
       else $log.error('angular-toastr.js: issue#109');
     });
});

needed to do it twice in angular-toastr.js when not using angular-toastr.tpls.min.js

@ruisilva450
Copy link

When can this be resolved and merged: #178

@Foxandxss
Copy link
Owner

I never confirmed that that fix works.

I am sorry for not responding, but my job is soo consuming lately.

@danielcmorris
Copy link

THE FIX:

I've had this problem for ages and just ignored it, but I finally got to it. Someone here even pointed it out and it worked. $templateCache.

    app.config(function (toastrConfig:any) {
        angular.extend(toastrConfig, {
            allowHtml: false,
            closeButton: false,
            closeHtml: '<button>&times;</button>',
            extendedTimeOut: 1000,
            iconClasses: {
                error: 'toast-error',
                info: 'toast-info',
                success: 'toast-success',
                warning: 'toast-warning'
            },
            messageClass: 'toast-message',
            onHidden: null,
            onShown: null,
            onTap: null,
            progressBar: false,
            tapToDismiss: true,
            templates: {
                toast: 'toast.html',
                progressbar: 'progressbar.html'
            },
            timeOut: 5000,
            titleClass: 'toast-title',
            toastClass: 'toast'
        });
    });


    app.run(['$templateCache',function ($templateCache:any) {
        
        $templateCache.put('toast.html', `
            <div class="{{toastClass}} {{toastType}}" ng-click="tapToast()">
                <div ng-switch on="allowHtml">
                    <div ng-switch-default ng-if="title" class="{{titleClass}}" aria-label="{{title}}">{{title}}</div>
                    <div ng-switch-default class="{{messageClass}}" aria-label="{{message}}">{{message}}</div>
                    <div ng-switch-when="true" ng-if="title" class="{{titleClass}}" ng-bind-html="title"></div>
                    <div ng-switch-when="true" class="{{messageClass}}" ng-bind-html="message"></div>
                </div>
                <progress-bar ng-if="progressBar"></progress-bar>
            </div>`);

        $templateCache.put('progressbar.html', `<div class="toast-progress"></div>`);



    }]);

you'll notice that I changed the config so toast.html and progressbar.html have no paths.
Then I force toast.html and progressbar.html into the cache.

I think the problem was simply that the template just didn't load fast enough. Was driving me nuts, because I couldn't get it to happen on my local server, but the production machine had the bug.

Anyway, if you're having trouble, try just cutting and pasting that code into yours. If it works, boom, you're done.

@a-samad
Copy link

a-samad commented Nov 22, 2017

Solution provided by danielcmorris is working, I have tested it and deployed in Production.

@trodi
Copy link

trodi commented Jul 8, 2019

A slight clarification to @danielcmorris solution:
You only need to add your custom templates to the $templateCache, as long as you use the tpls version (angular-toastr.tpls.js). The tpls version adds the defaults to the $templateCache for you. If you do this, be sure not to override the default template path in toastrConfig.templates for the template you not overriding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests