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

ui-sref initialze controller twice if url has params #1476

Closed
ruslansavenok opened this issue Oct 23, 2014 · 50 comments
Closed

ui-sref initialze controller twice if url has params #1476

ruslansavenok opened this issue Oct 23, 2014 · 50 comments
Assignees

Comments

@ruslansavenok
Copy link

Using ui-router v0.2.11

My Html

<li ui-sref-active="active">
  <a ui-sref="flash_lessons">Flash Lessons</a>
</li>
<li ui-sref-active="active">
  <a ui-sref="lessons">Lessons</a>
</li>

My coffee

$stateProvider
      .state 'flash_lessons',
        url: '/flash_lessons/:level_name'
        controller: () ->
          console.log 'will call me 2 times'

      .state 'lessons',
        url: '/lessons/'
        templateUrl: '/views/lessons/list.html'
        controller: 'LessonsListCtrl'

Opening lessons state, and then going to flash_lessons using ui-sref="flash_lessons" will initialize controller twice.

@nateabele
Copy link
Contributor

Please test this against the latest release.

@ruslansavenok
Copy link
Author

@nateabele
Copy link
Contributor

Ah okay, maybe I was wrong about it already being out in a release. @christopherthielen can you weigh in here?

@christopherthielen
Copy link
Contributor

I believe this is fixed in master, but not 0.2.12-pre1

@ruslansavenok
Copy link
Author

@christopherthielen my initial bug report was about version 0.2.11 which is current master state. I will prepare demo using 0.2.11 today

@christopherthielen
Copy link
Contributor

Master needs to be built manually.

@ruslansavenok
Copy link
Author

@christopherthielen just made 2 builds with 'grunt build' (for master & master-0.2.12-pr1).

Both of them has that bug. Updated demo & zip file are located on prev links.

@christopherthielen
Copy link
Contributor

Thanks I'll look at this early next week

@christopherthielen christopherthielen self-assigned this Oct 28, 2014
@ognus
Copy link

ognus commented Nov 12, 2014

Came across this bug as well, ui-router 0.2.11 and angularjs 1.3.2. From my observations, a controller is called twice only when the url parameter is defined but not passed in ui-sref (a.k.a optional route parameters), exactly like in @ruslansavenok's example.

@christopherthielen
Copy link
Contributor

@ognus thanks that helps.

The first transition via ui-sref="lessons" calls $state.go('lessons', undefined). This updates the URL to /lessons/. Then, the $locationChangeSuccess handler kicks in and matches /lessons/ to lessons {param_name: ""}, then calls $state.go('lessons', {param_name: ""}). Since the parameter has changed from undefined to "", the controller is re-invoked.

I'm wondering why we allow transitions when the required parameters are not provided. I have a TODO somewhere about aborting transitions when non-optional parameters are not provided.

@ruslansavenok
Copy link
Author

So the solution for now is to pass that argument as an empty string, works for my case :)

Thx @ognus @christopherthielen

@christopherthielen
Copy link
Contributor

Note: 0.2.12 is coming in the next few days (unless I find any other show stoppers). 0.2.12 will allow you to specify { param_name: "" } if you want the empty string as the default param value. Build from master if you want this now.

@ognus
Copy link

ognus commented Nov 12, 2014

@christopherthielen briliiant! thank you for that. Thanks @ruslansavenok for hack suggestion, works fine for me as well and will probably stick with just passing empty string till 0.2.12 release.

@christopherthielen
Copy link
Contributor

I've decided that 0.2.12 will continue to map undefined path parameters as an empty string, like it has done since at least 0.2.0. 0.3.0 will error if you try to transition without all required parameters.

http://plnkr.co/edit/RbPLBCVxNWdpIvJVlqId?p=preview

@eswak
Copy link

eswak commented Jan 27, 2015

For those who stumble upon this issue after it has been closed, chances are you googled "angular ui router optional parameter controller loaded twice" or something like this...

here is a recap of how it can be fixed :

Let's say you have a state with an optional parameter :

.state('app.numbers', {
  url: '/numbers{timestamp:.*}',
  templateUrl: 'numbers/templates/app.numbers.html',
  controller: 'NumberListCtrl'
})

if you just use a link like this, the controller NumberListCtrl will be loaded twice, once with a timestamp parameter with an undefined value, and once with a '' value :

<a ui-sref=".numbers">numbers</a>

To prevent your controller from being loaded twice, you can use the following methods :

<a ui-sref=".numbers({timestamp:''})">numbers</a>

or use $state in js :

$state.go('app.numbers', {timestamp: ''});

or (DIRTY 💩) add these lines at the top of your controller :

// prevent double controller load because of the optional parameter
if (typeof $stateParams.timestamp === 'undefined') {
   return;
}

Hope this helps.

@ghost
Copy link

ghost commented May 14, 2015

@eswak Thanks a lot, very useful.

@orbitbot
Copy link

Thanks for the tips, but I'm still affected by this somehow on 0.2.15. I'm using the $state.go approach and actually passing all optional parameters, but still end up loading the controller twice. With this version the $stateParams are not undefined so the controller workaround approach doesn't work.

I'm using nested states with my controller setup, don't know if that might have any impact.

@christopherthielen
Copy link
Contributor

Orbitbot post a plunker showing the issue please

@kathygit
Copy link

I have the same issue with 0.2.15. For me, it breaks when I have 3 params in the ui-sref, regardless or router setup.

html:
<a ui-sref="backup-job-details({p1:uuid, p2:serverId, p3:sid})"
If remove p3:sid, the controller is called just once.
If it has 3 or more params, the controller init is called twice.

e.g. var jobsDetail = {
name: 'backup-job-details',
url: '/job-details/:p1/:p2', --- regardless the params count here.
controller: 'JobsDetailCtrl',
templateUrl: 'app/jobs/jobsDetailView.html'
};

@CrazyPandar
Copy link

I have the same problem with 0.2.15

@kathygit
Copy link

Actually, the root cause for it is NOT the count the params, but one of the param has a slash in it. It works fine after I encoded param first.

@redi-madhava
Copy link

I had the same issue, that one of the param values has a slash in it, thus the controller is invoked twice: once with encoded param and the other time without encoding. This is tested with ui router v0.2.15 today.

html page:
View Child 1

angular routing js file snippet:
.state('blah', {
url: '/blah?path&time',
templateUrl: 'app/components/blah/blah.html',
controller: 'BlahController',
controllerAs: 'that'
});

@CrazyPandar
Copy link

Oh ,that is it!there is a slash in my param too。

On Wed, Sep 30, 2015, 4:53 AM Madhav Ayyagari notifications@github.com
wrote:

I had the same issue, that one of the param values has a slash in it, thus
the controller is invoked twice: once with encoded param and the other time
without encoding. This is tested with ui router v0.2.15 today.

html page:
View Child 1

angular routing js file snippet:
.state('blah', {
url: '/blah?path&time',
templateUrl: 'app/components/blah/blah.html',
controller: 'BlahController',
controllerAs: 'that'
});


Reply to this email directly or view it on GitHub
#1476 (comment)
.

@germanferrero
Copy link

I have similar issue here:
my state:
{
state: 'activate_user',
config: {
url: '/activate_user/:validation',
templateUrl: 'app/client/user-activation.html',
controller: 'activateUserController',
controllerAs: 'vm',
}
},

Navigating to:
http://localhost:3000/activate_user/bCUksWF1sSUCfCdRfsxEOif4VjkVNm3jROYU6pfJ2jTjR
provoke activateUserController to load twice. Please help.

UPDATE

I had two ui-view directives in my template. One of them was inside a ng-if="false" (after evaluating) div. Anyway that was causing my controller loading twice.
I changed ui-view with ui-view='viewName'
and my state like this:
{
state: 'activate_user',
config: {
url: '/activate_user/:validation',
views:{
viewName:{
templateUrl: 'app/client/user-activation.html',
controller: 'activateUserController',
controllerAs: 'vm'
}},}
},

@cvn
Copy link
Contributor

cvn commented Oct 27, 2015

I opened a new issue for the problem where the controller runs twice when one of the params has a slash.

#2339

@manishsingh2508
Copy link

Hello,

I am facing the similar issue. the controller is called twice which is causing a major problems.
I am using the 0.2.15 version.

I am surprised the issue still exists . Can anyone help me how can i resolve the issue. The parameter passed here is mandatory and it can be Y or N.

See the example.
Template
ui-sref=".newProject({fromTempl:fromTemplate})

Configuration
.state('home.projTypeView',{
url: '/projTypeView/:fromTempl',
views: {
'mainContainerView@': {
templateUrl: 'apps/views/projectTypeList.html',
controller:'projectTypeListController',

          }
     }
 })

Please help me out . I could not find any answer .
Regards,
Manish Singh

@ShritejSalvi7
Copy link

hi, using ui-router in angularjs i have nested the route but the problem is if i have assign controller to parent and i want to define it in different js file. I mean i dont want to define controller in app.js then what is the method

@shivasingh0101
Copy link

This problem still exists in ui-router 0.2.15 along with angular js 1.4.8 version. Which cause severe problem in whole application. Is there any workaround for this?

@shivasingh0101
Copy link

But there is no any slash's in my application.

@jongunter
Copy link

+1 Still having this issue. No slash in my route param.

@christopherthielen
Copy link
Contributor

If this problem persists, post a plunker to a new ticket that demonstrates the issue. Feel free to reference back to this ticket number 1476.

Posting +1 is completely unactionable unless you provide a reproduction

@jongunter
Copy link

My apologies, @christopherthielen . I'm trying to get a plunker working but I'm having trouble replicating the issue on its own. I'll open a new ticket once I can get more debugging information. Thanks!

@eswak
Copy link

eswak commented Feb 11, 2016

@jongunter did you try the method I described earlier in the conversation ? I included code snippets that don't have slash in the route param

@jongunter
Copy link

@eswak I tried the method and it didn't work. After a week of debugging, I found my issue was related to having an unnamed view in the page with no templateUrl.

Edit: oops sorry tagged the wrong person.

@anilvangari
Copy link

I created a plunker to reproduce the issue. It is caused by v0.2.15 which is what I have. When I upgraded to v0.2.18 the issue seems to be resolved.
Anyway the plunker is here.
https://plnkr.co/edit/4ibkGI49r2k0GBWjWKFe?p=preview

@76200
Copy link

76200 commented Mar 21, 2016

@christopherthielen, unfortunately the bug still exists with v0.2.18. Even with plunker shown above. Here's my config:

.state('document', {
    abstract: true,
    url: '/document',
    template: '<div ui-view>',
    data: {
        authorizedRoles: [USER_ROLES.all]
    }
})

...

.state('document.resources', {
    url: '/:documentId',
    parent: 'document',
    templateUrl: 'views/document/list.html',
    controller: 'DocumentCtrl'
})

.state('document.users', {
    url: '/:documentId',
    parent: 'document',
    templateUrl: 'views/document/list.html',
    controller: 'DocumentCtrl'
})

Funny thing is that "document.resources" creates controller once, but any further controllers are being created twice.

@christopherthielen
Copy link
Contributor

@76200 post a plunker if you can recreate the issue.

@76200
Copy link

76200 commented Mar 21, 2016

@christopherthielen
Copy link
Contributor

@76200 if you switch that plunker to 0.2.18 it works:
https://plnkr.co/edit/VO35zIacxJwyCHYHeQD0?p=preview

@76200
Copy link

76200 commented Mar 21, 2016

Yep, right. Sorry. I'll try to reproduce it later

@76200
Copy link

76200 commented Mar 22, 2016

Ok @christopherthielen I've reproduced it. In my application I have to create some states at runtime.
Run this plunker with console and click through all links. Two last links causes calling controllers twice.

https://plnkr.co/edit/hfamcJHGs8gB7PHYHgy3

@christopherthielen
Copy link
Contributor

@76200 thanks for the plunker that plainly demonstrates the issue.

You've defined multiple states with the same url pattern. In general, this is a questionable practice. For example, if the user goes to document.user state, the url might be /document/abc123. Then they bookmark that URL and come back. How is the router supposed to know if /document/abc123 should be document.user or document.resource? Generally url to state mapping should be unambiguous. I recommend setting your url to something unambiguous like /u/ for user or /r/ for resource.

However, I do agree that after transitioning to a target state via ui-sref, then transitioning to the other state that also matches the URL is surprising behavior. Since that's a separate issue (with somewhat similar symptoms), can you open a separate ticket and add your plunker?

We may be able to add a check when syncing the URL that avoids a new transition if the URL matches the current state. That would allow you to keep your ambiguous urls


On a side note, you're also doing some weird things in your state defs like specifying parent: document but naming the state document.user.edit, and repeating URL segments and parameters /:documentName... that could come from the natural parent state document.user

@cesiya23
Copy link

Hey guys, I am using "angular-ui-router": "^1.0.0-alpha.3" for my project, and I have the same problem that my controller for state with params is initialized twice.

How to fix this actually?

@patrickhanf
Copy link

Keep in mind that if you have a controller referenced both ui-route state and in the HTML file it will initialized twice. Alternatively, the controller: property could have been removed from the state routing directive., Example based on angular-ui-router/0.3.1 (7/20/16)

  .state('contacts', {
          url: '/contacts',
          templateUrl: '/contacts.html',
          controller: 'contactsCtrl'
      })

Remove extra ng-controller from contacts.html

<div ng-controller="contactsCtrl"></div>

This may help solve a few issues around this topic.

@sachabest
Copy link

Having a similar issue where

        .state('main.topic', {
            url: '/topic/:id',
            templateUrl: 'html/topic.html',
            controller: 'TopicController',
            controllerAs: 'ctrl',
            // ...

causes a clusterbomb of page-load duplicate errors, but

        .state('main.topic', {
            url: '/topic?id',
            templateUrl: 'html/topic.html',
            controller: 'TopicController',
            controllerAs: 'ctrl',
            // ...

does not. Versions 0.3.2 and 0.4.1. I am doing something obviously wrong here?

@christopherthielen
Copy link
Contributor

@sachabest shouldn't be problematic. Can you put the code in a plunker to reproduce the problem?

http://bit.ly/UIR-Plunk

@maghidini
Copy link

maghidini commented Feb 23, 2017

Hey guys, I'm having an issue related to this one, my resolve state is called twice. The thing is I am moving my ID based urls to a SEO friendly strings urls. I already changed my API to be able to response if I query with an ID or a slug name. But my router is getting the response using the slug name (this-is-an-example) but then the state is called again using the ID of the (this-is-an-example) document and overwriting the url with the ID instead of the slug. (Even refreshing the page and typing manually the slugName into the URL)

$stateProvider .state('conquest.events.event', { url: '/:eventId', resolve: { event: function (Event, $stateParams) { return Event.one($stateParams.eventId).get(); } }, views: { 'content@': { templateUrl: 'app/routes/events/event/event.html', controller: 'EventCtrl as eventCtrl' } } })

Using 2.18 router version and angular 1.4.12

@tramel-woodard
Copy link

Same here. Very small demo:

angular.module('myApp', ['ui.router'])
    .config(function($stateProvider) {
        var homeState = {
            name: 'home',
            url: '/',
            templateUrl: 'resolve_home.html'
        }
        var productState = {
            name: 'product',
            url: '/product',
            templateUrl: 'resolve_product.html'
        }
        $stateProvider.state(homeState);
        $stateProvider.state(productState);
    })
    .controller('myController', function($scope) {
        
    })
    .controller('productController', function($scope) {
        console.log('product is loaded');
    });

I've noticed some lag time in one of my larger applications, so I have been building AngularJS apps from the ground up and noticed that even in such a small application, the controller and its scoped functions are called twice (please assume that the HTML and angular.js and angular-ui-router.js files are functioning as normal).

When I open the console, the line 'product is loaded' is called twice. I have no idea what's going on. Not a deal breaker, but for a large application, lag will be noticeable.

Any reason for this? Seems like a non-invasive block of code for controllers to be called twice.

@christopherthielen
Copy link
Contributor

it's not normal. Please create a plunker.

http://bit.ly/UIR-Plunk

@zatchgordon
Copy link

zatchgordon commented May 15, 2017

I'm having the same problem running 0.2.18
I can't seem to reproduce the double controller loading but along with this double load problem I'm having an "onExit" problem as well.

foo : abstract
foo.bar : has /:urlVar
foo.bar.baz: returns user to foo.bar with a different /:urlVar

During this transition back the "onExit" function is called on the abstract view "foo"
But if the :urlVar is NOT changed and the user is returned to foo.bar, the "onExit" function is NOT called

http://plnkr.co/edit/tGzHwGJmD7TIqJYyIaFm?p=preview

Edit: updating to 1.0.3 solved the double loading problem... but it's seems to still be calling the "onExit" but now it calls the "onExit" and then the "resolve" so it's not so much of a problem

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